2
1

plugins.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import * as express from 'express'
  2. import { body, param, query, ValidationChain } from 'express-validator'
  3. import { logger } from '../../helpers/logger'
  4. import { areValidationErrors } from './utils'
  5. import { isNpmPluginNameValid, isPluginNameValid, isPluginTypeValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins'
  6. import { PluginManager } from '../../lib/plugins/plugin-manager'
  7. import { isBooleanValid, isSafePath, toBooleanOrNull, exists, toIntOrNull } from '../../helpers/custom-validators/misc'
  8. import { PluginModel } from '../../models/server/plugin'
  9. import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/install-plugin.model'
  10. import { PluginType } from '../../../shared/models/plugins/plugin.type'
  11. import { CONFIG } from '../../initializers/config'
  12. import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
  13. const getPluginValidator = (pluginType: PluginType, withVersion = true) => {
  14. const validators: (ValidationChain | express.Handler)[] = [
  15. param('pluginName').custom(isPluginNameValid).withMessage('Should have a valid plugin name')
  16. ]
  17. if (withVersion) {
  18. validators.push(
  19. param('pluginVersion').custom(isPluginVersionValid).withMessage('Should have a valid plugin version')
  20. )
  21. }
  22. return validators.concat([
  23. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  24. logger.debug('Checking getPluginValidator parameters', { parameters: req.params })
  25. if (areValidationErrors(req, res)) return
  26. const npmName = PluginModel.buildNpmName(req.params.pluginName, pluginType)
  27. const plugin = PluginManager.Instance.getRegisteredPluginOrTheme(npmName)
  28. if (!plugin) return res.sendStatus(HttpStatusCode.NOT_FOUND_404)
  29. if (withVersion && plugin.version !== req.params.pluginVersion) return res.sendStatus(HttpStatusCode.NOT_FOUND_404)
  30. res.locals.registeredPlugin = plugin
  31. return next()
  32. }
  33. ])
  34. }
  35. const getExternalAuthValidator = [
  36. param('authName').custom(exists).withMessage('Should have a valid auth name'),
  37. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  38. logger.debug('Checking getExternalAuthValidator parameters', { parameters: req.params })
  39. if (areValidationErrors(req, res)) return
  40. const plugin = res.locals.registeredPlugin
  41. if (!plugin.registerHelpersStore) return res.sendStatus(HttpStatusCode.NOT_FOUND_404)
  42. const externalAuth = plugin.registerHelpersStore.getExternalAuths().find(a => a.authName === req.params.authName)
  43. if (!externalAuth) return res.sendStatus(HttpStatusCode.NOT_FOUND_404)
  44. res.locals.externalAuth = externalAuth
  45. return next()
  46. }
  47. ]
  48. const pluginStaticDirectoryValidator = [
  49. param('staticEndpoint').custom(isSafePath).withMessage('Should have a valid static endpoint'),
  50. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  51. logger.debug('Checking pluginStaticDirectoryValidator parameters', { parameters: req.params })
  52. if (areValidationErrors(req, res)) return
  53. return next()
  54. }
  55. ]
  56. const listPluginsValidator = [
  57. query('pluginType')
  58. .optional()
  59. .customSanitizer(toIntOrNull)
  60. .custom(isPluginTypeValid).withMessage('Should have a valid plugin type'),
  61. query('uninstalled')
  62. .optional()
  63. .customSanitizer(toBooleanOrNull)
  64. .custom(isBooleanValid).withMessage('Should have a valid uninstalled attribute'),
  65. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  66. logger.debug('Checking listPluginsValidator parameters', { parameters: req.query })
  67. if (areValidationErrors(req, res)) return
  68. return next()
  69. }
  70. ]
  71. const installOrUpdatePluginValidator = [
  72. body('npmName')
  73. .optional()
  74. .custom(isNpmPluginNameValid).withMessage('Should have a valid npm name'),
  75. body('path')
  76. .optional()
  77. .custom(isSafePath).withMessage('Should have a valid safe path'),
  78. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  79. logger.debug('Checking installOrUpdatePluginValidator parameters', { parameters: req.body })
  80. if (areValidationErrors(req, res)) return
  81. const body: InstallOrUpdatePlugin = req.body
  82. if (!body.path && !body.npmName) {
  83. return res.status(HttpStatusCode.BAD_REQUEST_400)
  84. .json({ error: 'Should have either a npmName or a path' })
  85. .end()
  86. }
  87. return next()
  88. }
  89. ]
  90. const uninstallPluginValidator = [
  91. body('npmName').custom(isNpmPluginNameValid).withMessage('Should have a valid npm name'),
  92. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  93. logger.debug('Checking uninstallPluginValidator parameters', { parameters: req.body })
  94. if (areValidationErrors(req, res)) return
  95. return next()
  96. }
  97. ]
  98. const existingPluginValidator = [
  99. param('npmName').custom(isNpmPluginNameValid).withMessage('Should have a valid plugin name'),
  100. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  101. logger.debug('Checking enabledPluginValidator parameters', { parameters: req.params })
  102. if (areValidationErrors(req, res)) return
  103. const plugin = await PluginModel.loadByNpmName(req.params.npmName)
  104. if (!plugin) {
  105. return res.status(HttpStatusCode.NOT_FOUND_404)
  106. .json({ error: 'Plugin not found' })
  107. .end()
  108. }
  109. res.locals.plugin = plugin
  110. return next()
  111. }
  112. ]
  113. const updatePluginSettingsValidator = [
  114. body('settings').exists().withMessage('Should have settings'),
  115. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  116. logger.debug('Checking enabledPluginValidator parameters', { parameters: req.body })
  117. if (areValidationErrors(req, res)) return
  118. return next()
  119. }
  120. ]
  121. const listAvailablePluginsValidator = [
  122. query('search')
  123. .optional()
  124. .exists().withMessage('Should have a valid search'),
  125. query('pluginType')
  126. .optional()
  127. .customSanitizer(toIntOrNull)
  128. .custom(isPluginTypeValid).withMessage('Should have a valid plugin type'),
  129. query('currentPeerTubeEngine')
  130. .optional()
  131. .custom(isPluginVersionValid).withMessage('Should have a valid current peertube engine'),
  132. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  133. logger.debug('Checking enabledPluginValidator parameters', { parameters: req.query })
  134. if (areValidationErrors(req, res)) return
  135. if (CONFIG.PLUGINS.INDEX.ENABLED === false) {
  136. return res.status(HttpStatusCode.BAD_REQUEST_400)
  137. .json({ error: 'Plugin index is not enabled' })
  138. .end()
  139. }
  140. return next()
  141. }
  142. ]
  143. // ---------------------------------------------------------------------------
  144. export {
  145. pluginStaticDirectoryValidator,
  146. getPluginValidator,
  147. updatePluginSettingsValidator,
  148. uninstallPluginValidator,
  149. listAvailablePluginsValidator,
  150. existingPluginValidator,
  151. installOrUpdatePluginValidator,
  152. listPluginsValidator,
  153. getExternalAuthValidator
  154. }