login.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. // disable all of linting
  2. // because this file is a deprecated legacy of Crowi
  3. /* eslint-disable */
  4. module.exports = function(crowi, app) {
  5. const debug = require('debug')('growi:routes:login');
  6. const logger = require('@alias/logger')('growi:routes:login');
  7. const path = require('path');
  8. const async = require('async');
  9. const config = crowi.getConfig();
  10. const mailer = crowi.getMailer();
  11. const User = crowi.model('User');
  12. const Config = crowi.model('Config');
  13. const actions = {};
  14. const clearGoogleSession = function(req) {
  15. req.session.googleAuthCode = req.session.googleId = req.session.googleEmail = req.session.googleName = req.session.googleImage = null;
  16. };
  17. const loginSuccess = function(req, res, userData) {
  18. req.user = req.session.user = userData;
  19. // update lastLoginAt
  20. userData.updateLastLoginAt(new Date(), (err, uData) => {
  21. if (err) {
  22. logger.error(`updateLastLoginAt dumps error: ${err}`);
  23. }
  24. });
  25. if (!userData.password) {
  26. return res.redirect('/me/password');
  27. }
  28. clearGoogleSession(req);
  29. const jumpTo = req.session.jumpTo;
  30. if (jumpTo) {
  31. req.session.jumpTo = null;
  32. // prevention from open redirect
  33. if (!jumpTo.match(/^\/\/.+$/)) {
  34. return res.redirect(jumpTo);
  35. }
  36. }
  37. return res.redirect('/');
  38. };
  39. const loginFailure = function(req, res) {
  40. req.flash('warningMessage', 'Sign in failure.');
  41. return res.redirect('/login');
  42. };
  43. actions.googleCallback = function(req, res) {
  44. const nextAction = req.session.googleCallbackAction || '/login';
  45. debug('googleCallback.nextAction', nextAction);
  46. req.session.googleAuthCode = req.query.code || '';
  47. debug('google auth code', req.query.code);
  48. return res.redirect(nextAction);
  49. };
  50. actions.error = function(req, res) {
  51. const reason = req.params.reason;
  52. let reasonMessage = '';
  53. if (reason === 'suspended') {
  54. reasonMessage = 'This account is suspended.';
  55. }
  56. else if (reason === 'registered') {
  57. reasonMessage = 'Wait for approved by administrators.';
  58. }
  59. return res.render('login/error', {
  60. reason,
  61. reasonMessage,
  62. });
  63. };
  64. actions.login = function(req, res) {
  65. const loginForm = req.body.loginForm;
  66. if (req.method == 'POST' && req.form.isValid) {
  67. const username = loginForm.username;
  68. const password = loginForm.password;
  69. // find user
  70. User.findUserByUsernameOrEmail(username, password, (err, user) => {
  71. if (err) { return loginFailure(req, res) }
  72. // check existence and password
  73. if (!user || !user.isPasswordValid(password)) {
  74. return loginFailure(req, res);
  75. }
  76. return loginSuccess(req, res, user);
  77. });
  78. }
  79. else { // method GET
  80. if (req.form) {
  81. debug(req.form.errors);
  82. }
  83. return res.render('login', {
  84. });
  85. }
  86. };
  87. actions.loginGoogle = function(req, res) {
  88. const googleAuth = require('../util/googleAuth')(crowi);
  89. const code = req.session.googleAuthCode || null;
  90. if (!code) {
  91. googleAuth.createAuthUrl(req, (err, redirectUrl) => {
  92. if (err) {
  93. // TODO
  94. }
  95. req.session.googleCallbackAction = '/login/google';
  96. return res.redirect(redirectUrl);
  97. });
  98. }
  99. else {
  100. googleAuth.handleCallback(req, (err, tokenInfo) => {
  101. debug('handleCallback', err, tokenInfo);
  102. if (err) {
  103. return loginFailure(req, res);
  104. }
  105. const googleId = tokenInfo.user_id;
  106. User.findUserByGoogleId(googleId, (err, userData) => {
  107. debug('findUserByGoogleId', err, userData);
  108. if (!userData) {
  109. clearGoogleSession(req);
  110. return loginFailure(req, res);
  111. }
  112. return loginSuccess(req, res, userData);
  113. });
  114. });
  115. }
  116. };
  117. actions.register = function(req, res) {
  118. const googleAuth = require('../util/googleAuth')(crowi);
  119. // redirect to '/' if both of these are true:
  120. // 1. user has logged in
  121. // 2. req.user is not username/email string (which is set by basic-auth-connect)
  122. if (req.user != null && req.user instanceof Object) {
  123. return res.redirect('/');
  124. }
  125. // config で closed ならさよなら
  126. if (config.crowi['security:registrationMode'] == Config.SECURITY_REGISTRATION_MODE_CLOSED) {
  127. return res.redirect('/');
  128. }
  129. if (req.method == 'POST' && req.form.isValid) {
  130. const registerForm = req.form.registerForm || {};
  131. const name = registerForm.name;
  132. const username = registerForm.username;
  133. const email = registerForm.email;
  134. const password = registerForm.password;
  135. var googleId = registerForm.googleId || null;
  136. var googleImage = registerForm.googleImage || null;
  137. // email と username の unique チェックする
  138. User.isRegisterable(email, username, (isRegisterable, errOn) => {
  139. let isError = false;
  140. if (!User.isEmailValid(email)) {
  141. isError = true;
  142. req.flash('registerWarningMessage', 'This email address could not be used. (Make sure the allowed email address)');
  143. }
  144. if (!isRegisterable) {
  145. if (!errOn.username) {
  146. isError = true;
  147. req.flash('registerWarningMessage', 'This User ID is not available.');
  148. }
  149. if (!errOn.email) {
  150. isError = true;
  151. req.flash('registerWarningMessage', 'This email address is already registered.');
  152. }
  153. }
  154. if (isError) {
  155. debug('isError user register error', errOn);
  156. return res.redirect('/register');
  157. }
  158. User.createUserByEmailAndPassword(name, username, email, password, undefined, (err, userData) => {
  159. if (err) {
  160. if (err.name === 'UserUpperLimitException') {
  161. req.flash('registerWarningMessage', 'Can not register more than the maximum number of users.');
  162. }
  163. else {
  164. req.flash('registerWarningMessage', 'Failed to register.');
  165. }
  166. return res.redirect('/register');
  167. }
  168. // 作成後、承認が必要なモードなら、管理者に通知する
  169. const appTitle = Config.appTitle(config);
  170. if (config.crowi['security:registrationMode'] === Config.SECURITY_REGISTRATION_MODE_RESTRICTED) {
  171. // TODO send mail
  172. User.findAdmins((err, admins) => {
  173. async.each(
  174. admins,
  175. (adminUser, next) => {
  176. mailer.send({
  177. to: adminUser.email,
  178. subject: `[${appTitle}:admin] A New User Created and Waiting for Activation`,
  179. template: path.join(crowi.localeDir, 'en-US/admin/userWaitingActivation.txt'),
  180. vars: {
  181. createdUser: userData,
  182. adminUser,
  183. url: crowi.configManager.getSiteUrl(),
  184. appTitle,
  185. },
  186. },
  187. (err, s) => {
  188. debug('completed to send email: ', err, s);
  189. next();
  190. });
  191. },
  192. (err) => {
  193. debug('Sending invitation email completed.', err);
  194. },
  195. );
  196. });
  197. }
  198. if (googleId) {
  199. userData.updateGoogleId(googleId, (err, userData) => {
  200. if (err) { // TODO
  201. }
  202. return loginSuccess(req, res, userData);
  203. });
  204. }
  205. else {
  206. // add a flash message to inform the user that processing was successful -- 2017.09.23 Yuki Takei
  207. // cz. loginSuccess method doesn't work on it's own when using passport
  208. // because `req.login()` prepared by passport is not called.
  209. req.flash('successMessage', `The user '${userData.username}' is successfully created.`);
  210. return loginSuccess(req, res, userData);
  211. }
  212. });
  213. });
  214. }
  215. else { // method GET of form is not valid
  216. debug('session is', req.session);
  217. const isRegistering = true;
  218. // google callback を受ける可能性もある
  219. const code = req.session.googleAuthCode || null;
  220. var googleId = req.session.googleId || null;
  221. let googleEmail = req.session.googleEmail || null;
  222. let googleName = req.session.googleName || null;
  223. var googleImage = req.session.googleImage || null;
  224. debug('register. if code', code);
  225. // callback 経由で reigster にアクセスしてきた時最初だけこの if に入る
  226. // code から email などを取得したらそれを session にいれて code は消去
  227. if (code) {
  228. googleAuth.handleCallback(req, (err, tokenInfo) => {
  229. debug('tokenInfo on register GET', tokenInfo);
  230. req.session.googleAuthCode = null;
  231. if (err) {
  232. req.flash('registerWarningMessage', 'Error on connectiong Google');
  233. return res.redirect('/login?register=1'); // TODO Handling
  234. }
  235. req.session.googleId = googleId = tokenInfo.user_id;
  236. req.session.googleEmail = googleEmail = tokenInfo.email;
  237. req.session.googleName = googleName = tokenInfo.name;
  238. req.session.googleImage = googleImage = tokenInfo.picture;
  239. if (!User.isEmailValid(googleEmail)) {
  240. req.flash('registerWarningMessage', 'このメールアドレスのGoogleアカウントはコネクトできません。');
  241. return res.redirect('/login?register=1');
  242. }
  243. return res.render('login', {
  244. isRegistering, googleId, googleEmail, googleName, googleImage,
  245. });
  246. });
  247. }
  248. else {
  249. return res.render('login', {
  250. isRegistering, googleId, googleEmail, googleName, googleImage,
  251. });
  252. }
  253. }
  254. };
  255. actions.registerGoogle = function(req, res) {
  256. const googleAuth = require('../util/googleAuth')(crowi);
  257. googleAuth.createAuthUrl(req, (err, redirectUrl) => {
  258. if (err) {
  259. // TODO
  260. }
  261. req.session.googleCallbackAction = '/register';
  262. return res.redirect(redirectUrl);
  263. });
  264. };
  265. actions.invited = async function(req, res) {
  266. if (!req.user) {
  267. return res.redirect('/login');
  268. }
  269. if (req.method == 'POST' && req.form.isValid) {
  270. const user = req.user;
  271. const invitedForm = req.form.invitedForm || {};
  272. const username = invitedForm.username;
  273. const name = invitedForm.name;
  274. const password = invitedForm.password;
  275. // check user upper limit
  276. const isUserCountExceedsUpperLimit = await User.isUserCountExceedsUpperLimit();
  277. if (isUserCountExceedsUpperLimit) {
  278. req.flash('warningMessage', 'ユーザーが上限に達したためアクティベートできません。');
  279. return res.redirect('/invited');
  280. }
  281. const creatable = await User.isRegisterableUsername(username);
  282. if (creatable) {
  283. try {
  284. await user.activateInvitedUser(username, name, password);
  285. return res.redirect('/');
  286. }
  287. catch (err) {
  288. req.flash('warningMessage', 'アクティベートに失敗しました。');
  289. return res.render('invited');
  290. }
  291. }
  292. else {
  293. req.flash('warningMessage', '利用できないユーザーIDです。');
  294. debug('username', username);
  295. return res.render('invited');
  296. }
  297. }
  298. else {
  299. return res.render('invited', {
  300. });
  301. }
  302. };
  303. actions.updateInvitedUser = function(req, res) {
  304. return res.redirect('/');
  305. };
  306. return actions;
  307. };