aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorJelle Besseling <jelle@pingiun.com>2021-10-12 13:33:44 +0200
committerGitHub <noreply@github.com>2021-10-12 13:33:44 +0200
commit8d8a037e3fe9b1d2ccbc4169ce59b13000b59cb0 (patch)
tree755ba56bc3acbd82ec195974545581c1e49aae5e /server
parentbadacdbb4a3e4a1aae4d324abc496be8e261b2ef (diff)
downloadPeerTube-8d8a037e3fe9b1d2ccbc4169ce59b13000b59cb0.tar.gz
PeerTube-8d8a037e3fe9b1d2ccbc4169ce59b13000b59cb0.tar.zst
PeerTube-8d8a037e3fe9b1d2ccbc4169ce59b13000b59cb0.zip
Allow configuration to be static/readonly (#4315)
* Allow configuration to be static/readonly * Make all components disableable * Improve disabled component styling * Rename edits allowed field in configuration * Fix CI
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/config.ts4
-rw-r--r--server/initializers/config.ts31
-rw-r--r--server/lib/server-config-manager.ts1
-rw-r--r--server/middlewares/validators/config.ts16
-rw-r--r--server/tests/api/server/config.ts364
5 files changed, 234 insertions, 182 deletions
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts
index d542f62aa..5ea1f67c9 100644
--- a/server/controllers/api/config.ts
+++ b/server/controllers/api/config.ts
@@ -11,7 +11,7 @@ import { objectConverter } from '../../helpers/core-utils'
11import { CONFIG, reloadConfig } from '../../initializers/config' 11import { CONFIG, reloadConfig } from '../../initializers/config'
12import { ClientHtml } from '../../lib/client-html' 12import { ClientHtml } from '../../lib/client-html'
13import { asyncMiddleware, authenticate, ensureUserHasRight, openapiOperationDoc } from '../../middlewares' 13import { asyncMiddleware, authenticate, ensureUserHasRight, openapiOperationDoc } from '../../middlewares'
14import { customConfigUpdateValidator } from '../../middlewares/validators/config' 14import { customConfigUpdateValidator, ensureConfigIsEditable } from '../../middlewares/validators/config'
15 15
16const configRouter = express.Router() 16const configRouter = express.Router()
17 17
@@ -38,6 +38,7 @@ configRouter.put('/custom',
38 openapiOperationDoc({ operationId: 'putCustomConfig' }), 38 openapiOperationDoc({ operationId: 'putCustomConfig' }),
39 authenticate, 39 authenticate,
40 ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), 40 ensureUserHasRight(UserRight.MANAGE_CONFIGURATION),
41 ensureConfigIsEditable,
41 customConfigUpdateValidator, 42 customConfigUpdateValidator,
42 asyncMiddleware(updateCustomConfig) 43 asyncMiddleware(updateCustomConfig)
43) 44)
@@ -46,6 +47,7 @@ configRouter.delete('/custom',
46 openapiOperationDoc({ operationId: 'delCustomConfig' }), 47 openapiOperationDoc({ operationId: 'delCustomConfig' }),
47 authenticate, 48 authenticate,
48 ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), 49 ensureUserHasRight(UserRight.MANAGE_CONFIGURATION),
50 ensureConfigIsEditable,
49 asyncMiddleware(deleteCustomConfig) 51 asyncMiddleware(deleteCustomConfig)
50) 52)
51 53
diff --git a/server/initializers/config.ts b/server/initializers/config.ts
index be9fc61f0..b2a8e9e19 100644
--- a/server/initializers/config.ts
+++ b/server/initializers/config.ts
@@ -195,6 +195,13 @@ const CONFIG = {
195 URL: config.get<string>('peertube.check_latest_version.url') 195 URL: config.get<string>('peertube.check_latest_version.url')
196 } 196 }
197 }, 197 },
198 WEBADMIN: {
199 CONFIGURATION: {
200 EDITS: {
201 ALLOWED: config.get<boolean>('webadmin.configuration.edit.allowed')
202 }
203 }
204 },
198 ADMIN: { 205 ADMIN: {
199 get EMAIL () { return config.get<string>('admin.email') } 206 get EMAIL () { return config.get<string>('admin.email') }
200 }, 207 },
@@ -411,14 +418,22 @@ export {
411// --------------------------------------------------------------------------- 418// ---------------------------------------------------------------------------
412 419
413function getLocalConfigFilePath () { 420function getLocalConfigFilePath () {
414 const configSources = config.util.getConfigSources() 421 const localConfigDir = getLocalConfigDir()
415 if (configSources.length === 0) throw new Error('Invalid config source.')
416 422
417 let filename = 'local' 423 let filename = 'local'
418 if (process.env.NODE_ENV) filename += `-${process.env.NODE_ENV}` 424 if (process.env.NODE_ENV) filename += `-${process.env.NODE_ENV}`
419 if (process.env.NODE_APP_INSTANCE) filename += `-${process.env.NODE_APP_INSTANCE}` 425 if (process.env.NODE_APP_INSTANCE) filename += `-${process.env.NODE_APP_INSTANCE}`
420 426
421 return join(dirname(configSources[0].name), filename + '.json') 427 return join(localConfigDir, filename + '.json')
428}
429
430function getLocalConfigDir () {
431 if (process.env.PEERTUBE_LOCAL_CONFIG) return process.env.PEERTUBE_LOCAL_CONFIG
432
433 const configSources = config.util.getConfigSources()
434 if (configSources.length === 0) throw new Error('Invalid config source.')
435
436 return dirname(configSources[0].name)
422} 437}
423 438
424function buildVideosRedundancy (objs: any[]): VideosRedundancyStrategy[] { 439function buildVideosRedundancy (objs: any[]): VideosRedundancyStrategy[] {
@@ -437,19 +452,19 @@ function buildVideosRedundancy (objs: any[]): VideosRedundancyStrategy[] {
437 452
438export function reloadConfig () { 453export function reloadConfig () {
439 454
440 function getConfigDirectory () { 455 function getConfigDirectories () {
441 if (process.env.NODE_CONFIG_DIR) { 456 if (process.env.NODE_CONFIG_DIR) {
442 return process.env.NODE_CONFIG_DIR 457 return process.env.NODE_CONFIG_DIR.split(":")
443 } 458 }
444 459
445 return join(root(), 'config') 460 return [ join(root(), 'config') ]
446 } 461 }
447 462
448 function purge () { 463 function purge () {
449 const directory = getConfigDirectory() 464 const directories = getConfigDirectories()
450 465
451 for (const fileName in require.cache) { 466 for (const fileName in require.cache) {
452 if (fileName.includes(directory) === false) { 467 if (directories.some((dir) => fileName.includes(dir)) === false) {
453 continue 468 continue
454 } 469 }
455 470
diff --git a/server/lib/server-config-manager.ts b/server/lib/server-config-manager.ts
index 80d87a9d3..358f47133 100644
--- a/server/lib/server-config-manager.ts
+++ b/server/lib/server-config-manager.ts
@@ -42,6 +42,7 @@ class ServerConfigManager {
42 const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME) 42 const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME)
43 43
44 return { 44 return {
45 allowEdits: CONFIG.WEBADMIN.CONFIGURATION.EDITS.ALLOWED,
45 instance: { 46 instance: {
46 name: CONFIG.INSTANCE.NAME, 47 name: CONFIG.INSTANCE.NAME,
47 shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION, 48 shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION,
diff --git a/server/middlewares/validators/config.ts b/server/middlewares/validators/config.ts
index 16a840667..5f1ac89bc 100644
--- a/server/middlewares/validators/config.ts
+++ b/server/middlewares/validators/config.ts
@@ -1,13 +1,14 @@
1import express from 'express' 1import express from 'express'
2import { body } from 'express-validator' 2import { body } from 'express-validator'
3import { isIntOrNull } from '@server/helpers/custom-validators/misc' 3import { isIntOrNull } from '@server/helpers/custom-validators/misc'
4import { isEmailEnabled } from '@server/initializers/config' 4import { CONFIG, isEmailEnabled } from '@server/initializers/config'
5import { CustomConfig } from '../../../shared/models/server/custom-config.model' 5import { CustomConfig } from '../../../shared/models/server/custom-config.model'
6import { isThemeNameValid } from '../../helpers/custom-validators/plugins' 6import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
7import { isUserNSFWPolicyValid, isUserVideoQuotaDailyValid, isUserVideoQuotaValid } from '../../helpers/custom-validators/users' 7import { isUserNSFWPolicyValid, isUserVideoQuotaDailyValid, isUserVideoQuotaValid } from '../../helpers/custom-validators/users'
8import { logger } from '../../helpers/logger' 8import { logger } from '../../helpers/logger'
9import { isThemeRegistered } from '../../lib/plugins/theme-utils' 9import { isThemeRegistered } from '../../lib/plugins/theme-utils'
10import { areValidationErrors } from './shared' 10import { areValidationErrors } from './shared'
11import { HttpStatusCode } from '@shared/models/http/http-error-codes'
11 12
12const customConfigUpdateValidator = [ 13const customConfigUpdateValidator = [
13 body('instance.name').exists().withMessage('Should have a valid instance name'), 14 body('instance.name').exists().withMessage('Should have a valid instance name'),
@@ -104,10 +105,21 @@ const customConfigUpdateValidator = [
104 } 105 }
105] 106]
106 107
108function ensureConfigIsEditable (req: express.Request, res: express.Response, next: express.NextFunction) {
109 if (!CONFIG.WEBADMIN.CONFIGURATION.EDITS.ALLOWED) {
110 return res.fail({
111 status: HttpStatusCode.METHOD_NOT_ALLOWED_405,
112 message: 'Server configuration is static and cannot be edited'
113 })
114 }
115 return next()
116}
117
107// --------------------------------------------------------------------------- 118// ---------------------------------------------------------------------------
108 119
109export { 120export {
110 customConfigUpdateValidator 121 customConfigUpdateValidator,
122 ensureConfigIsEditable
111} 123}
112 124
113function checkInvalidConfigIfEmailDisabled (customConfig: CustomConfig, res: express.Response) { 125function checkInvalidConfigIfEmailDisabled (customConfig: CustomConfig, res: express.Response) {
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts
index c4dd882b8..e057ec1a2 100644
--- a/server/tests/api/server/config.ts
+++ b/server/tests/api/server/config.ts
@@ -201,6 +201,199 @@ function checkUpdatedConfig (data: CustomConfig) {
201 expect(data.broadcastMessage.dismissable).to.be.true 201 expect(data.broadcastMessage.dismissable).to.be.true
202} 202}
203 203
204const newCustomConfig: CustomConfig = {
205 instance: {
206 name: 'PeerTube updated',
207 shortDescription: 'my short description',
208 description: 'my super description',
209 terms: 'my super terms',
210 codeOfConduct: 'my super coc',
211
212 creationReason: 'my super creation reason',
213 moderationInformation: 'my super moderation information',
214 administrator: 'Kuja',
215 maintenanceLifetime: 'forever',
216 businessModel: 'my super business model',
217 hardwareInformation: '2vCore 3GB RAM',
218
219 languages: [ 'en', 'es' ],
220 categories: [ 1, 2 ],
221
222 isNSFW: true,
223 defaultNSFWPolicy: 'blur' as 'blur',
224
225 defaultClientRoute: '/videos/recently-added',
226
227 customizations: {
228 javascript: 'alert("coucou")',
229 css: 'body { background-color: red; }'
230 }
231 },
232 theme: {
233 default: 'default'
234 },
235 services: {
236 twitter: {
237 username: '@Kuja',
238 whitelisted: true
239 }
240 },
241 cache: {
242 previews: {
243 size: 2
244 },
245 captions: {
246 size: 3
247 },
248 torrents: {
249 size: 4
250 }
251 },
252 signup: {
253 enabled: false,
254 limit: 5,
255 requiresEmailVerification: false,
256 minimumAge: 10
257 },
258 admin: {
259 email: 'superadmin1@example.com'
260 },
261 contactForm: {
262 enabled: false
263 },
264 user: {
265 videoQuota: 5242881,
266 videoQuotaDaily: 318742
267 },
268 transcoding: {
269 enabled: true,
270 allowAdditionalExtensions: true,
271 allowAudioFiles: true,
272 threads: 1,
273 concurrency: 3,
274 profile: 'vod_profile',
275 resolutions: {
276 '0p': false,
277 '240p': false,
278 '360p': true,
279 '480p': true,
280 '720p': false,
281 '1080p': false,
282 '1440p': false,
283 '2160p': false
284 },
285 webtorrent: {
286 enabled: true
287 },
288 hls: {
289 enabled: false
290 }
291 },
292 live: {
293 enabled: true,
294 allowReplay: true,
295 maxDuration: 5000,
296 maxInstanceLives: -1,
297 maxUserLives: 10,
298 transcoding: {
299 enabled: true,
300 threads: 4,
301 profile: 'live_profile',
302 resolutions: {
303 '240p': true,
304 '360p': true,
305 '480p': true,
306 '720p': true,
307 '1080p': true,
308 '1440p': true,
309 '2160p': true
310 }
311 }
312 },
313 import: {
314 videos: {
315 concurrency: 4,
316 http: {
317 enabled: false
318 },
319 torrent: {
320 enabled: false
321 }
322 }
323 },
324 trending: {
325 videos: {
326 algorithms: {
327 enabled: [ 'best', 'hot', 'most-viewed', 'most-liked' ],
328 default: 'hot'
329 }
330 }
331 },
332 autoBlacklist: {
333 videos: {
334 ofUsers: {
335 enabled: true
336 }
337 }
338 },
339 followers: {
340 instance: {
341 enabled: false,
342 manualApproval: true
343 }
344 },
345 followings: {
346 instance: {
347 autoFollowBack: {
348 enabled: true
349 },
350 autoFollowIndex: {
351 enabled: true,
352 indexUrl: 'https://updated.example.com'
353 }
354 }
355 },
356 broadcastMessage: {
357 enabled: true,
358 level: 'error',
359 message: 'super bad message',
360 dismissable: true
361 },
362 search: {
363 remoteUri: {
364 anonymous: true,
365 users: true
366 },
367 searchIndex: {
368 enabled: true,
369 url: 'https://search.joinpeertube.org',
370 disableLocalSearch: true,
371 isDefaultSearch: true
372 }
373 }
374}
375
376describe('Test static config', function () {
377 let server: PeerTubeServer = null
378
379 before(async function () {
380 this.timeout(30000)
381
382 server = await createSingleServer(1, { webadmin: { configuration: { edit: { allowed: false } } } })
383 await setAccessTokensToServers([ server ])
384 })
385
386 it('Should tell the client that edits are not allowed', async function () {
387 const data = await server.config.getConfig()
388
389 expect(data.allowEdits).to.be.false
390 })
391
392 it('Should error when client tries to update', async function () {
393 await server.config.updateCustomConfig({ newCustomConfig, expectedStatus: 405 })
394 })
395})
396
204describe('Test config', function () { 397describe('Test config', function () {
205 let server: PeerTubeServer = null 398 let server: PeerTubeServer = null
206 399
@@ -252,177 +445,6 @@ describe('Test config', function () {
252 }) 445 })
253 446
254 it('Should update the customized configuration', async function () { 447 it('Should update the customized configuration', async function () {
255 const newCustomConfig: CustomConfig = {
256 instance: {
257 name: 'PeerTube updated',
258 shortDescription: 'my short description',
259 description: 'my super description',
260 terms: 'my super terms',
261 codeOfConduct: 'my super coc',
262
263 creationReason: 'my super creation reason',
264 moderationInformation: 'my super moderation information',
265 administrator: 'Kuja',
266 maintenanceLifetime: 'forever',
267 businessModel: 'my super business model',
268 hardwareInformation: '2vCore 3GB RAM',
269
270 languages: [ 'en', 'es' ],
271 categories: [ 1, 2 ],
272
273 isNSFW: true,
274 defaultNSFWPolicy: 'blur' as 'blur',
275
276 defaultClientRoute: '/videos/recently-added',
277
278 customizations: {
279 javascript: 'alert("coucou")',
280 css: 'body { background-color: red; }'
281 }
282 },
283 theme: {
284 default: 'default'
285 },
286 services: {
287 twitter: {
288 username: '@Kuja',
289 whitelisted: true
290 }
291 },
292 cache: {
293 previews: {
294 size: 2
295 },
296 captions: {
297 size: 3
298 },
299 torrents: {
300 size: 4
301 }
302 },
303 signup: {
304 enabled: false,
305 limit: 5,
306 requiresEmailVerification: false,
307 minimumAge: 10
308 },
309 admin: {
310 email: 'superadmin1@example.com'
311 },
312 contactForm: {
313 enabled: false
314 },
315 user: {
316 videoQuota: 5242881,
317 videoQuotaDaily: 318742
318 },
319 transcoding: {
320 enabled: true,
321 allowAdditionalExtensions: true,
322 allowAudioFiles: true,
323 threads: 1,
324 concurrency: 3,
325 profile: 'vod_profile',
326 resolutions: {
327 '0p': false,
328 '240p': false,
329 '360p': true,
330 '480p': true,
331 '720p': false,
332 '1080p': false,
333 '1440p': false,
334 '2160p': false
335 },
336 webtorrent: {
337 enabled: true
338 },
339 hls: {
340 enabled: false
341 }
342 },
343 live: {
344 enabled: true,
345 allowReplay: true,
346 maxDuration: 5000,
347 maxInstanceLives: -1,
348 maxUserLives: 10,
349 transcoding: {
350 enabled: true,
351 threads: 4,
352 profile: 'live_profile',
353 resolutions: {
354 '240p': true,
355 '360p': true,
356 '480p': true,
357 '720p': true,
358 '1080p': true,
359 '1440p': true,
360 '2160p': true
361 }
362 }
363 },
364 import: {
365 videos: {
366 concurrency: 4,
367 http: {
368 enabled: false
369 },
370 torrent: {
371 enabled: false
372 }
373 }
374 },
375 trending: {
376 videos: {
377 algorithms: {
378 enabled: [ 'best', 'hot', 'most-viewed', 'most-liked' ],
379 default: 'hot'
380 }
381 }
382 },
383 autoBlacklist: {
384 videos: {
385 ofUsers: {
386 enabled: true
387 }
388 }
389 },
390 followers: {
391 instance: {
392 enabled: false,
393 manualApproval: true
394 }
395 },
396 followings: {
397 instance: {
398 autoFollowBack: {
399 enabled: true
400 },
401 autoFollowIndex: {
402 enabled: true,
403 indexUrl: 'https://updated.example.com'
404 }
405 }
406 },
407 broadcastMessage: {
408 enabled: true,
409 level: 'error',
410 message: 'super bad message',
411 dismissable: true
412 },
413 search: {
414 remoteUri: {
415 anonymous: true,
416 users: true
417 },
418 searchIndex: {
419 enabled: true,
420 url: 'https://search.joinpeertube.org',
421 disableLocalSearch: true,
422 isDefaultSearch: true
423 }
424 }
425 }
426 await server.config.updateCustomConfig({ newCustomConfig }) 448 await server.config.updateCustomConfig({ newCustomConfig })
427 449
428 const data = await server.config.getCustomConfig() 450 const data = await server.config.getCustomConfig()