login.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import { SupportedAction, SupportedTargetModel } from '~/interfaces/activity';
  2. import { configManager } from '~/server/service/config-manager';
  3. import loggerFactory from '~/utils/logger';
  4. import { growiInfoService } from '../service/growi-info';
  5. // disable all of linting
  6. // because this file is a deprecated legacy of Crowi
  7. /** @param {import('~/server/crowi').default} crowi Crowi instance */
  8. module.exports = (crowi, app) => {
  9. const logger = loggerFactory('growi:routes:login');
  10. const path = require('path');
  11. const User = crowi.model('User');
  12. const { appService, aclService, mailService, activityService } = crowi;
  13. const activityEvent = crowi.event('activity');
  14. const actions = {};
  15. async function sendEmailToAllAdmins(userData) {
  16. // send mails to all admin users (derived from crowi) -- 2020.06.18 Yuki Takei
  17. const admins = await User.findAdmins();
  18. const appTitle = appService.getAppTitle();
  19. const locale = configManager.getConfig('app:globalLang');
  20. const promises = admins.map((admin) => {
  21. return mailService.send({
  22. to: admin.email,
  23. subject: `[${appTitle}:admin] A New User Created and Waiting for Activation`,
  24. template: path.join(
  25. crowi.localeDir,
  26. `${locale}/admin/userWaitingActivation.ejs`,
  27. ),
  28. vars: {
  29. adminUser: admin,
  30. createdUser: userData,
  31. url: growiInfoService.getSiteUrl(),
  32. appTitle,
  33. },
  34. });
  35. });
  36. const results = await Promise.allSettled(promises);
  37. results
  38. .filter((result) => result.status === 'rejected')
  39. .forEach((result) => {
  40. logger.error(result.reason);
  41. });
  42. }
  43. async function sendNotificationToAllAdmins(user) {
  44. const activity = await activityService.createActivity({
  45. action: SupportedAction.ACTION_USER_REGISTRATION_APPROVAL_REQUEST,
  46. target: user,
  47. targetModel: SupportedTargetModel.MODEL_USER,
  48. });
  49. /**
  50. * @param {import('../service/pre-notify').PreNotifyProps} props
  51. */
  52. const preNotify = async (props) => {
  53. /** @type {(import('mongoose').HydratedDocument<import('@growi/core').IUser>)[]} */
  54. const adminUsers = await User.findAdmins();
  55. const { notificationTargetUsers } = props;
  56. notificationTargetUsers?.push(...adminUsers);
  57. };
  58. await activityEvent.emit('updated', activity, user, preNotify);
  59. return;
  60. }
  61. const registerSuccessHandler = async (
  62. req,
  63. res,
  64. userData,
  65. registrationMode,
  66. ) => {
  67. const parameters = {
  68. action: SupportedAction.ACTION_USER_REGISTRATION_SUCCESS,
  69. };
  70. activityEvent.emit('update', res.locals.activity._id, parameters);
  71. const isMailerSetup = mailService.isMailerSetup ?? false;
  72. if (
  73. registrationMode ===
  74. aclService.labels.SECURITY_REGISTRATION_MODE_RESTRICTED
  75. ) {
  76. sendNotificationToAllAdmins(userData);
  77. if (isMailerSetup) {
  78. await sendEmailToAllAdmins(userData);
  79. }
  80. return res.apiv3({});
  81. }
  82. /**
  83. * @swagger
  84. *
  85. * /login:
  86. * post:
  87. * summary: /login
  88. * tags: [Users]
  89. * requestBody:
  90. * required: true
  91. * content:
  92. * application/json:
  93. * schema:
  94. * type: object
  95. * properties:
  96. * loginForm:
  97. * type: object
  98. * properties:
  99. * username:
  100. * type: string
  101. * password:
  102. * type: string
  103. * responses:
  104. * 200:
  105. * description: Login successful
  106. * content:
  107. * application/json:
  108. * schema:
  109. * type: object
  110. * properties:
  111. * redirectTo:
  112. * type: string
  113. */
  114. req.login(userData, (err) => {
  115. if (err) {
  116. logger.debug(err);
  117. } else {
  118. // update lastLoginAt
  119. userData.updateLastLoginAt(new Date(), (err) => {
  120. if (err) {
  121. logger.error(`updateLastLoginAt dumps error: ${err}`);
  122. }
  123. });
  124. }
  125. let redirectTo;
  126. if (userData.password == null) {
  127. // userData.password can't be empty but, prepare redirect because password property in User Model is optional
  128. // https://github.com/growilabs/growi/pull/6670
  129. redirectTo = '/me#password_settings';
  130. } else if (req.session.redirectTo != null) {
  131. redirectTo = req.session.redirectTo;
  132. delete req.session.redirectTo;
  133. } else {
  134. redirectTo = '/';
  135. }
  136. return res.apiv3({ redirectTo });
  137. });
  138. };
  139. actions.preLogin = (req, res, next) => {
  140. // user has already logged in
  141. const { user } = req;
  142. if (user != null && user.status === User.STATUS_ACTIVE) {
  143. const { redirectTo } = req.session;
  144. // remove session.redirectTo
  145. delete req.session.redirectTo;
  146. return res.safeRedirect(redirectTo);
  147. }
  148. // set referer to 'redirectTo'
  149. if (req.session.redirectTo == null && req.headers.referer != null) {
  150. req.session.redirectTo = req.headers.referer;
  151. }
  152. next();
  153. };
  154. /**
  155. * @swagger
  156. *
  157. * /register:
  158. * post:
  159. * summary: /register
  160. * tags: [Users]
  161. * requestBody:
  162. * required: true
  163. * content:
  164. * application/json:
  165. * schema:
  166. * type: object
  167. * properties:
  168. * registerForm:
  169. * type: object
  170. * properties:
  171. * name:
  172. * type: string
  173. * username:
  174. * type: string
  175. * email:
  176. * type: string
  177. * password:
  178. * type: string
  179. * responses:
  180. * 200:
  181. * description: Register successful
  182. * content:
  183. * application/json:
  184. * schema:
  185. * type: object
  186. * properties:
  187. * redirectTo:
  188. * type: string
  189. */
  190. actions.register = (req, res) => {
  191. if (req.user != null) {
  192. return res.apiv3Err('message.user_already_logged_in', 403);
  193. }
  194. // config で closed ならさよなら
  195. if (
  196. configManager.getConfig('security:registrationMode') ===
  197. aclService.labels.SECURITY_REGISTRATION_MODE_CLOSED
  198. ) {
  199. return res.apiv3Err('message.registration_closed', 403);
  200. }
  201. if (!req.form.isValid) {
  202. const errors = req.form.errors;
  203. return res.apiv3Err(errors, 400);
  204. }
  205. const registerForm = req.form.registerForm || {};
  206. const name = registerForm.name;
  207. const username = registerForm.username;
  208. const email = registerForm.email;
  209. const password = registerForm.password;
  210. // email と username の unique チェックする
  211. User.isRegisterable(email, username, (isRegisterable, errOn) => {
  212. const errors = [];
  213. if (!User.isEmailValid(email)) {
  214. errors.push('message.email_address_could_not_be_used');
  215. }
  216. if (!isRegisterable) {
  217. if (!errOn.username) {
  218. errors.push('message.user_id_is_not_available');
  219. }
  220. if (!errOn.email) {
  221. errors.push('message.email_address_is_already_registered');
  222. }
  223. }
  224. if (errors.length > 0) {
  225. logger.debug('isError user register error', errOn);
  226. return res.apiv3Err(errors, 400);
  227. }
  228. const registrationMode = configManager.getConfig(
  229. 'security:registrationMode',
  230. );
  231. User.createUserByEmailAndPassword(
  232. name,
  233. username,
  234. email,
  235. password,
  236. undefined,
  237. async (err, userData) => {
  238. if (err) {
  239. const errors = [];
  240. if (err.name === 'UserUpperLimitException') {
  241. errors.push('message.can_not_register_maximum_number_of_users');
  242. } else {
  243. errors.push('message.failed_to_register');
  244. }
  245. return res.apiv3Err(errors, 405);
  246. }
  247. return registerSuccessHandler(req, res, userData, registrationMode);
  248. },
  249. );
  250. });
  251. };
  252. return actions;
  253. };