login.js 8.2 KB

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