personal-setting.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /* eslint-disable no-unused-vars */
  2. const loggerFactory = require('@alias/logger');
  3. const logger = loggerFactory('growi:routes:apiv3:personal-setting');
  4. const express = require('express');
  5. const passport = require('passport');
  6. const router = express.Router();
  7. const { body } = require('express-validator/check');
  8. const ErrorV3 = require('../../models/vo/error-apiv3');
  9. /**
  10. * @swagger
  11. * tags:
  12. * name: PsersonalSetting
  13. */
  14. /**
  15. * @swagger
  16. *
  17. * components:
  18. * schemas:
  19. * PersonalSettings:
  20. * description: personal settings
  21. * type: object
  22. * properties:
  23. * name:
  24. * type: string
  25. * email:
  26. * type: string
  27. * lang:
  28. * type: string
  29. * isEmailPublished:
  30. * type: boolean
  31. * Passwords:
  32. * description: passwords for update
  33. * type: object
  34. * properties:
  35. * oldPassword:
  36. * type: string
  37. * newPassword:
  38. * type: string
  39. * newPasswordConfirm:
  40. * type: string
  41. */
  42. module.exports = (crowi) => {
  43. const accessTokenParser = require('../../middleware/access-token-parser')(crowi);
  44. const loginRequiredStrictly = require('../../middleware/login-required')(crowi);
  45. const csrf = require('../../middleware/csrf')(crowi);
  46. const { User, ExternalAccount } = crowi.models;
  47. const { ApiV3FormValidator } = crowi.middlewares;
  48. const validator = {
  49. personal: [
  50. body('name').isString().not().isEmpty(),
  51. body('email').isEmail(),
  52. body('lang').isString().isIn(['en-US', 'ja']),
  53. body('isEmailPublished').isBoolean(),
  54. ],
  55. imageType: [
  56. body('isGravatarEnabled').isBoolean(),
  57. ],
  58. password: [
  59. body('oldPassword').isString(),
  60. body('newPassword').isString().not().isEmpty()
  61. .isLength({ min: 6 })
  62. .withMessage('password must be at least 6 characters long'),
  63. body('newPasswordConfirm').isString().not().isEmpty()
  64. .custom((value, { req }) => {
  65. return (value === req.body.newPassword);
  66. }),
  67. ],
  68. };
  69. /**
  70. * @swagger
  71. *
  72. * /personal-setting:
  73. * get:
  74. * tags: [PersonalSetting]
  75. * operationId: getPersonalSetting
  76. * summary: /personal-setting
  77. * description: Get personal parameters
  78. * responses:
  79. * 200:
  80. * description: params of personal
  81. * content:
  82. * application/json:
  83. * schema:
  84. * properties:
  85. * currentUser:
  86. * type: object
  87. * description: personal params
  88. */
  89. router.get('/', accessTokenParser, loginRequiredStrictly, async(req, res) => {
  90. const currentUser = await User.findUserByUsername(req.user.username);
  91. return res.apiv3({ currentUser });
  92. });
  93. /**
  94. * @swagger
  95. *
  96. * /personal-setting:
  97. * put:
  98. * tags: [PersonalSetting]
  99. * operationId: updatePersonalSetting
  100. * summary: /personal-setting
  101. * description: Update personal setting
  102. * requestBody:
  103. * required: true
  104. * content:
  105. * application/json:
  106. * schema:
  107. * $ref: '#/components/schemas/PersonalSettings'
  108. * responses:
  109. * 200:
  110. * description: params of personal
  111. * content:
  112. * application/json:
  113. * schema:
  114. * properties:
  115. * currentUser:
  116. * type: object
  117. * description: personal params
  118. */
  119. router.put('/', accessTokenParser, loginRequiredStrictly, csrf, validator.personal, ApiV3FormValidator, async(req, res) => {
  120. try {
  121. const user = await User.findOne({ _id: req.user.id });
  122. user.name = req.body.name;
  123. user.email = req.body.email;
  124. user.lang = req.body.lang;
  125. user.isEmailPublished = req.body.isEmailPublished;
  126. const updatedUser = await user.save();
  127. req.i18n.changeLanguage(req.body.lang);
  128. return res.apiv3({ updatedUser });
  129. }
  130. catch (err) {
  131. logger.error(err);
  132. return res.apiv3Err('update-personal-settings-failed');
  133. }
  134. });
  135. /**
  136. * @swagger
  137. *
  138. * /personal-setting/image-type:
  139. * put:
  140. * tags: [PersonalSetting]
  141. * operationId: putUserImageType
  142. * summary: /personal-setting/image-type
  143. * description: Update user image type
  144. * responses:
  145. * 200:
  146. * description: succeded to update user image type
  147. * content:
  148. * application/json:
  149. * schema:
  150. * properties:
  151. * userData:
  152. * type: object
  153. * description: user data
  154. */
  155. router.put('/image-type', accessTokenParser, loginRequiredStrictly, csrf, validator.imageType, ApiV3FormValidator, async(req, res) => {
  156. const { isGravatarEnabled } = req.body;
  157. try {
  158. const userData = await req.user.updateIsGravatarEnabled(isGravatarEnabled);
  159. return res.apiv3({ userData });
  160. }
  161. catch (err) {
  162. logger.error(err);
  163. return res.apiv3Err('update-personal-settings-failed');
  164. }
  165. });
  166. /**
  167. * @swagger
  168. *
  169. * /personal-setting/external-accounts:
  170. * get:
  171. * tags: [PersonalSetting]
  172. * operationId: getExternalAccounts
  173. * summary: /personal-setting/external-accounts
  174. * description: Get external accounts that linked current user
  175. * responses:
  176. * 200:
  177. * description: external accounts
  178. * content:
  179. * application/json:
  180. * schema:
  181. * properties:
  182. * externalAccounts:
  183. * type: object
  184. * description: array of external accounts
  185. */
  186. router.get('/external-accounts', accessTokenParser, loginRequiredStrictly, async(req, res) => {
  187. const userData = req.user;
  188. try {
  189. const externalAccounts = await ExternalAccount.find({ user: userData });
  190. return res.apiv3({ externalAccounts });
  191. }
  192. catch (err) {
  193. logger.error(err);
  194. return res.apiv3Err('get-external-accounts-failed');
  195. }
  196. });
  197. /**
  198. * @swagger
  199. *
  200. * /personal-setting/password:
  201. * put:
  202. * tags: [PersonalSetting]
  203. * operationId: putUserPassword
  204. * summary: /personal-setting/password
  205. * description: Update user password
  206. * requestBody:
  207. * required: true
  208. * content:
  209. * application/json:
  210. * schema:
  211. * $ref: '#/components/schemas/Passwords'
  212. * responses:
  213. * 200:
  214. * description: user password
  215. * content:
  216. * application/json:
  217. * schema:
  218. * properties:
  219. * userData:
  220. * type: object
  221. * description: user data updated
  222. */
  223. router.put('/password', accessTokenParser, loginRequiredStrictly, csrf, validator.password, ApiV3FormValidator, async(req, res) => {
  224. const { body, user } = req;
  225. const { oldPassword, newPassword } = body;
  226. if (user.isPasswordSet() && !user.isPasswordValid(oldPassword)) {
  227. return res.apiv3Err('wrong-current-password', 400);
  228. }
  229. try {
  230. const userData = await user.updatePassword(newPassword);
  231. return res.apiv3({ userData });
  232. }
  233. catch (err) {
  234. logger.error(err);
  235. return res.apiv3Err('update-password-failed');
  236. }
  237. });
  238. /**
  239. * @swagger
  240. *
  241. * /personal-setting/api-token:
  242. * put:
  243. * tags: [PersonalSetting]
  244. * operationId: putUserApiToken
  245. * summary: /personal-setting/api-token
  246. * description: Update user api token
  247. * responses:
  248. * 200:
  249. * description: succeded to update user api token
  250. * content:
  251. * application/json:
  252. * schema:
  253. * properties:
  254. * userData:
  255. * type: object
  256. * description: user data
  257. */
  258. router.put('/api-token', loginRequiredStrictly, csrf, async(req, res) => {
  259. const { user } = req;
  260. try {
  261. const userData = await user.updateApiToken();
  262. return res.apiv3({ userData });
  263. }
  264. catch (err) {
  265. logger.error(err);
  266. return res.apiv3Err('update-api-token-failed');
  267. }
  268. });
  269. // TODO swagger
  270. router.put('/associate-ldap', loginRequiredStrictly, csrf, async(req, res) => {
  271. const { passportService } = crowi;
  272. const { user, body } = req;
  273. const { username } = body;
  274. if (!passportService.isLdapStrategySetup) {
  275. logger.error('LdapStrategy has not been set up');
  276. return res.apiv3Err('associate-ldap-account-failed', 405);
  277. }
  278. try {
  279. await passport.authenticate('ldapauth');
  280. const associateUser = await ExternalAccount.associate('ldap', username, user);
  281. return res.apiv3({ associateUser });
  282. }
  283. catch (err) {
  284. logger.error(err);
  285. return res.apiv3Err('associate-ldap-account-failed');
  286. }
  287. });
  288. // TODO swagger
  289. router.put('/disassociate-ldap', loginRequiredStrictly, csrf, async(req, res) => {
  290. const { user, body } = req;
  291. const count = await ExternalAccount.count({ user });
  292. // make sure password set or this user has two or more ExternalAccounts
  293. if (user.password == null || count <= 1) {
  294. return res.apiv3Err('disassociate-ldap-account-failed');
  295. }
  296. return res.apiv3();
  297. });
  298. return router;
  299. };