markdown-setting.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. import { ErrorV3 } from '@growi/core/dist/models';
  2. import { SupportedAction } from '~/interfaces/activity';
  3. import { configManager } from '~/server/service/config-manager';
  4. import loggerFactory from '~/utils/logger';
  5. import { generateAddActivityMiddleware } from '../../middlewares/add-activity';
  6. import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
  7. const logger = loggerFactory('growi:routes:apiv3:markdown-setting');
  8. const express = require('express');
  9. const router = express.Router();
  10. const { body } = require('express-validator');
  11. const validator = {
  12. lineBreak: [
  13. body('isEnabledLinebreaks').isBoolean(),
  14. body('isEnabledLinebreaksInComments').isBoolean(),
  15. ],
  16. indent: [
  17. body('adminPreferredIndentSize').isIn([2, 4]),
  18. body('isIndentSizeForced').isBoolean(),
  19. ],
  20. xssSetting: [
  21. body('isEnabledXss').isBoolean(),
  22. body('tagWhitelist').isArray(),
  23. body('attrWhitelist').isString(),
  24. ],
  25. };
  26. /**
  27. * @swagger
  28. *
  29. * components:
  30. * schemas:
  31. * MarkdownParams:
  32. * description: MarkdownParams
  33. * type: object
  34. * properties:
  35. * isEnabledLinebreaks:
  36. * type: boolean
  37. * description: enable lineBreak
  38. * isEnabledLinebreaksInComments:
  39. * type: boolean
  40. * description: enable lineBreak in comment
  41. * adminPreferredIndentSize:
  42. * type: number
  43. * description: preferred indent size
  44. * isIndentSizeForced:
  45. * type: boolean
  46. * description: force indent size
  47. * isEnabledXss:
  48. * type: boolean
  49. * description: enable xss
  50. * xssOption:
  51. * type: number
  52. * description: number of xss option
  53. * tagWhitelist:
  54. * type: array
  55. * description: array of tag whitelist
  56. * items:
  57. * type: string
  58. * description: tag whitelist
  59. * attrWhitelist:
  60. * type: string
  61. * description: attr whitelist
  62. * LineBreakParams:
  63. * description: LineBreakParams
  64. * type: object
  65. * properties:
  66. * isEnabledLinebreaks:
  67. * type: boolean
  68. * description: enable lineBreak
  69. * isEnabledLinebreaksInComments:
  70. * type: boolean
  71. * description: enable lineBreak in comment
  72. * PresentationParams:
  73. * description: PresentationParams
  74. * type: object
  75. * properties:
  76. * pageBreakSeparator:
  77. * type: number
  78. * description: number of pageBreakSeparator
  79. * pageBreakCustomSeparator:
  80. * type: string
  81. * description: string of pageBreakCustomSeparator
  82. * XssParams:
  83. * description: XssParams
  84. * type: object
  85. * properties:
  86. * isEnabledXss:
  87. * type: boolean
  88. * description: enable xss
  89. * xssOption:
  90. * type: number
  91. * description: number of xss option
  92. * tagWhitelist:
  93. * type: array
  94. * description: array of tag whitelist
  95. * items:
  96. * type: string
  97. * description: tag whitelist
  98. * attrWhitelist:
  99. * type: string
  100. * description: attr whitelist
  101. * IndentParams:
  102. * description: IndentParams
  103. * type: object
  104. * properties:
  105. * adminPreferredIndentSize:
  106. * type: number
  107. * description: preferred indent size
  108. * isIndentSizeForced:
  109. * type: boolean
  110. * description: force indent size
  111. */
  112. /** @param {import('~/server/crowi').default} crowi Crowi instance */
  113. module.exports = (crowi) => {
  114. const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
  115. const adminRequired = require('../../middlewares/admin-required')(crowi);
  116. const addActivity = generateAddActivityMiddleware(crowi);
  117. const activityEvent = crowi.event('activity');
  118. /**
  119. * @swagger
  120. *
  121. * /markdown-setting:
  122. * get:
  123. * tags: [MarkDownSetting]
  124. * security:
  125. * - cookieAuth: []
  126. * summary: Get markdown parameters
  127. * responses:
  128. * 200:
  129. * description: params of markdown
  130. * content:
  131. * application/json:
  132. * schema:
  133. * properties:
  134. * markdownParams:
  135. * type: object
  136. * description: markdown params
  137. * $ref: '#/components/schemas/MarkdownParams'
  138. */
  139. router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => {
  140. const markdownParams = {
  141. isEnabledLinebreaks: await crowi.configManager.getConfig('markdown:isEnabledLinebreaks'),
  142. isEnabledLinebreaksInComments: await crowi.configManager.getConfig('markdown:isEnabledLinebreaksInComments'),
  143. adminPreferredIndentSize: await crowi.configManager.getConfig('markdown:adminPreferredIndentSize'),
  144. isIndentSizeForced: await crowi.configManager.getConfig('markdown:isIndentSizeForced'),
  145. isEnabledXss: await crowi.configManager.getConfig('markdown:rehypeSanitize:isEnabledPrevention'),
  146. xssOption: await crowi.configManager.getConfig('markdown:rehypeSanitize:option'),
  147. tagWhitelist: await crowi.configManager.getConfig('markdown:rehypeSanitize:tagNames'),
  148. attrWhitelist: await crowi.configManager.getConfig('markdown:rehypeSanitize:attributes'),
  149. };
  150. return res.apiv3({ markdownParams });
  151. });
  152. /**
  153. * @swagger
  154. *
  155. * /markdown-setting/lineBreak:
  156. * put:
  157. * tags: [MarkDownSetting]
  158. * security:
  159. * - cookieAuth: []
  160. * summary: Update lineBreak setting
  161. * requestBody:
  162. * required: true
  163. * content:
  164. * application/json:
  165. * schema:
  166. * $ref: '#/components/schemas/LineBreakParams'
  167. * responses:
  168. * 200:
  169. * description: Succeeded to update lineBreak setting
  170. * content:
  171. * application/json:
  172. * schema:
  173. * type: object
  174. * properties:
  175. * lineBreaksParams:
  176. * type: object
  177. * $ref: '#/components/schemas/LineBreakParams'
  178. */
  179. router.put('/lineBreak', loginRequiredStrictly, adminRequired, addActivity, validator.lineBreak, apiV3FormValidator, async(req, res) => {
  180. const requestLineBreakParams = {
  181. 'markdown:isEnabledLinebreaks': req.body.isEnabledLinebreaks,
  182. 'markdown:isEnabledLinebreaksInComments': req.body.isEnabledLinebreaksInComments,
  183. };
  184. try {
  185. await configManager.updateConfigs(requestLineBreakParams);
  186. const lineBreaksParams = {
  187. isEnabledLinebreaks: await crowi.configManager.getConfig('markdown:isEnabledLinebreaks'),
  188. isEnabledLinebreaksInComments: await crowi.configManager.getConfig('markdown:isEnabledLinebreaksInComments'),
  189. };
  190. const parameters = { action: SupportedAction.ACTION_ADMIN_MARKDOWN_LINE_BREAK_UPDATE };
  191. activityEvent.emit('update', res.locals.activity._id, parameters);
  192. return res.apiv3({ lineBreaksParams });
  193. }
  194. catch (err) {
  195. const msg = 'Error occurred in updating lineBreak';
  196. logger.error('Error', err);
  197. return res.apiv3Err(new ErrorV3(msg, 'update-lineBreak-failed'));
  198. }
  199. });
  200. /**
  201. * @swagger
  202. *
  203. * /markdown-setting/indent:
  204. * put:
  205. * tags: [MarkDownSetting]
  206. * security:
  207. * - cookieAuth: []
  208. * summary: Update indent setting
  209. * requestBody:
  210. * required: true
  211. * content:
  212. * application/json:
  213. * schema:
  214. * $ref: '#/components/schemas/IndentParams'
  215. * responses:
  216. * 200:
  217. * description: Succeeded to update indent setting
  218. * content:
  219. * application/json:
  220. * schema:
  221. * type: object
  222. * properties:
  223. * indentParams:
  224. * type: object
  225. * description: indent params
  226. * $ref: '#/components/schemas/IndentParams'
  227. */
  228. router.put('/indent', loginRequiredStrictly, adminRequired, addActivity, validator.indent, apiV3FormValidator, async(req, res) => {
  229. const requestIndentParams = {
  230. 'markdown:adminPreferredIndentSize': req.body.adminPreferredIndentSize,
  231. 'markdown:isIndentSizeForced': req.body.isIndentSizeForced,
  232. };
  233. try {
  234. await configManager.updateConfigs(requestIndentParams);
  235. const indentParams = {
  236. adminPreferredIndentSize: await crowi.configManager.getConfig('markdown:adminPreferredIndentSize'),
  237. isIndentSizeForced: await crowi.configManager.getConfig('markdown:isIndentSizeForced'),
  238. };
  239. const parameters = { action: SupportedAction.ACTION_ADMIN_MARKDOWN_INDENT_UPDATE };
  240. activityEvent.emit('update', res.locals.activity._id, parameters);
  241. return res.apiv3({ indentParams });
  242. }
  243. catch (err) {
  244. const msg = 'Error occurred in updating indent';
  245. logger.error('Error', err);
  246. return res.apiv3Err(new ErrorV3(msg, 'update-indent-failed'));
  247. }
  248. });
  249. /**
  250. * @swagger
  251. *
  252. * /markdown-setting/xss:
  253. * put:
  254. * tags: [MarkDownSetting]
  255. * security:
  256. * - cookieAuth: []
  257. * summary: Update XSS setting
  258. * description: Update xss
  259. * requestBody:
  260. * required: true
  261. * content:
  262. * application/json:
  263. * schema:
  264. * $ref: '#/components/schemas/XssParams'
  265. * responses:
  266. * 200:
  267. * description: Succeeded to update xss setting
  268. * content:
  269. * application/json:
  270. * schema:
  271. * $ref: '#/components/schemas/XssParams'
  272. */
  273. router.put('/xss', loginRequiredStrictly, adminRequired, addActivity, validator.xssSetting, apiV3FormValidator, async(req, res) => {
  274. if (req.body.isEnabledXss && req.body.xssOption == null) {
  275. return res.apiv3Err(new ErrorV3('xss option is required'));
  276. }
  277. try {
  278. JSON.parse(req.body.attrWhitelist);
  279. }
  280. catch (err) {
  281. const msg = 'Error occurred in updating xss';
  282. logger.error('Error', err);
  283. return res.apiv3Err(new ErrorV3(msg, 'update-xss-failed'));
  284. }
  285. const reqestXssParams = {
  286. 'markdown:rehypeSanitize:isEnabledPrevention': req.body.isEnabledXss,
  287. 'markdown:rehypeSanitize:option': req.body.xssOption,
  288. 'markdown:rehypeSanitize:tagNames': req.body.tagWhitelist,
  289. 'markdown:rehypeSanitize:attributes': req.body.attrWhitelist,
  290. };
  291. try {
  292. await configManager.updateConfigs(reqestXssParams);
  293. const xssParams = {
  294. isEnabledXss: await crowi.configManager.getConfig('markdown:rehypeSanitize:isEnabledPrevention'),
  295. xssOption: await crowi.configManager.getConfig('markdown:rehypeSanitize:option'),
  296. tagWhitelist: await crowi.configManager.getConfig('markdown:rehypeSanitize:tagNames'),
  297. attrWhitelist: await crowi.configManager.getConfig('markdown:rehypeSanitize:attributes'),
  298. };
  299. const parameters = { action: SupportedAction.ACTION_ADMIN_MARKDOWN_XSS_UPDATE };
  300. activityEvent.emit('update', res.locals.activity._id, parameters);
  301. return res.apiv3({ xssParams });
  302. }
  303. catch (err) {
  304. const msg = 'Error occurred in updating xss';
  305. logger.error('Error', err);
  306. return res.apiv3Err(new ErrorV3(msg, 'update-xss-failed'));
  307. }
  308. });
  309. return router;
  310. };