login-passport.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. module.exports = function(crowi, app) {
  2. 'use strict';
  3. var debug = require('debug')('growi:routes:login-passport')
  4. , logger = require('@alias/logger')('growi:routes:login-passport')
  5. , passport = require('passport')
  6. , config = crowi.getConfig()
  7. , Config = crowi.model('Config')
  8. , ExternalAccount = crowi.model('ExternalAccount')
  9. , passportService = crowi.passportService
  10. ;
  11. /**
  12. * success handler
  13. * @param {*} req
  14. * @param {*} res
  15. */
  16. const loginSuccess = (req, res, user) => {
  17. // update lastLoginAt
  18. user.updateLastLoginAt(new Date(), (err, userData) => {
  19. if (err) {
  20. logger.error(`updateLastLoginAt dumps error: ${err}`);
  21. debug(`updateLastLoginAt dumps error: ${err}`);
  22. }
  23. });
  24. var jumpTo = req.session.jumpTo;
  25. if (jumpTo) {
  26. req.session.jumpTo = null;
  27. return res.redirect(jumpTo);
  28. }
  29. else {
  30. return res.redirect('/');
  31. }
  32. };
  33. /**
  34. * failure handler
  35. * @param {*} req
  36. * @param {*} res
  37. */
  38. const loginFailure = (req, res, next) => {
  39. req.flash('errorMessage', 'Sign in failure.');
  40. return res.redirect('/login');
  41. };
  42. /**
  43. * return true(valid) or false(invalid)
  44. *
  45. * true ... group filter is not defined or the user has one or more groups
  46. * false ... group filter is defined and the user has any group
  47. *
  48. */
  49. function isValidLdapUserByGroupFilter(user) {
  50. let bool = true;
  51. if (user._groups != null) {
  52. if (user._groups.length == 0) {
  53. bool = false;
  54. }
  55. }
  56. return bool;
  57. }
  58. /**
  59. * middleware that login with LdapStrategy
  60. * @param {*} req
  61. * @param {*} res
  62. * @param {*} next
  63. */
  64. const loginWithLdap = (req, res, next) => {
  65. if (!passportService.isLdapStrategySetup) {
  66. debug('LdapStrategy has not been set up');
  67. return next();
  68. }
  69. if (!req.form.isValid) {
  70. debug('invalid form');
  71. return res.render('login', {
  72. });
  73. }
  74. passport.authenticate('ldapauth', (err, ldapAccountInfo, info) => {
  75. if (res.headersSent) { // dirty hack -- 2017.09.25
  76. return; // cz: somehow passport.authenticate called twice when ECONNREFUSED error occurred
  77. }
  78. debug('--- authenticate with LdapStrategy ---');
  79. debug('ldapAccountInfo', ldapAccountInfo);
  80. debug('info', info);
  81. if (err) { // DB Error
  82. logger.error('LDAP Server Error: ', err);
  83. req.flash('warningMessage', 'LDAP Server Error occured.');
  84. return next(); // pass and the flash message is displayed when all of authentications are failed.
  85. }
  86. // authentication failure
  87. if (!ldapAccountInfo) { return next() }
  88. // check groups
  89. if (!isValidLdapUserByGroupFilter(ldapAccountInfo)) {
  90. return loginFailure(req, res, next);
  91. }
  92. /*
  93. * authentication success
  94. */
  95. // it is guaranteed that username that is input from form can be acquired
  96. // because this processes after authentication
  97. const ldapAccountId = passportService.getLdapAccountIdFromReq(req);
  98. const attrMapUsername = passportService.getLdapAttrNameMappedToUsername();
  99. const attrMapName = passportService.getLdapAttrNameMappedToName();
  100. const usernameToBeRegistered = ldapAccountInfo[attrMapUsername];
  101. const nameToBeRegistered = ldapAccountInfo[attrMapName];
  102. // find or register(create) user
  103. ExternalAccount.findOrRegister('ldap', ldapAccountId, usernameToBeRegistered, nameToBeRegistered)
  104. .catch((err) => {
  105. if (err.name === 'DuplicatedUsernameException') {
  106. // get option
  107. const isSameUsernameTreatedAsIdenticalUser = Config.isSameUsernameTreatedAsIdenticalUser(config, 'ldap');
  108. if (isSameUsernameTreatedAsIdenticalUser) {
  109. // associate to existing user
  110. debug(`ExternalAccount '${ldapAccountId}' will be created and bound to the exisiting User account`);
  111. return ExternalAccount.associate('ldap', ldapAccountId, err.user);
  112. }
  113. }
  114. throw err; // throw again
  115. })
  116. .then((externalAccount) => {
  117. return externalAccount.getPopulatedUser();
  118. })
  119. .then((user) => {
  120. // login
  121. req.logIn(user, (err) => {
  122. if (err) { return next() }
  123. else {
  124. return loginSuccess(req, res, user);
  125. }
  126. });
  127. })
  128. .catch((err) => {
  129. if (err.name === 'DuplicatedUsernameException') {
  130. req.flash('isDuplicatedUsernameExceptionOccured', true);
  131. return next();
  132. }
  133. else {
  134. return next(err);
  135. }
  136. });
  137. })(req, res, next);
  138. };
  139. /**
  140. * middleware that test credentials with LdapStrategy
  141. *
  142. * @param {*} req
  143. * @param {*} res
  144. */
  145. const testLdapCredentials = (req, res) => {
  146. if (!passportService.isLdapStrategySetup) {
  147. debug('LdapStrategy has not been set up');
  148. return res.json({
  149. status: 'warning',
  150. message: 'LdapStrategy has not been set up',
  151. });
  152. }
  153. passport.authenticate('ldapauth', (err, user, info) => {
  154. if (res.headersSent) { // dirty hack -- 2017.09.25
  155. return; // cz: somehow passport.authenticate called twice when ECONNREFUSED error occurred
  156. }
  157. if (err) { // DB Error
  158. logger.error('LDAP Server Error: ', err);
  159. return res.json({
  160. status: 'warning',
  161. message: 'LDAP Server Error occured.',
  162. err
  163. });
  164. }
  165. if (info && info.message) {
  166. return res.json({
  167. status: 'warning',
  168. message: info.message,
  169. ldapConfiguration: req.ldapConfiguration,
  170. ldapAccountInfo: req.ldapAccountInfo,
  171. });
  172. }
  173. if (user) {
  174. // check groups
  175. if (!isValidLdapUserByGroupFilter(user)) {
  176. return res.json({
  177. status: 'warning',
  178. message: 'The user is found, but that has no groups.',
  179. ldapConfiguration: req.ldapConfiguration,
  180. ldapAccountInfo: req.ldapAccountInfo,
  181. });
  182. }
  183. return res.json({
  184. status: 'success',
  185. message: 'Successfully authenticated.',
  186. ldapConfiguration: req.ldapConfiguration,
  187. ldapAccountInfo: req.ldapAccountInfo,
  188. });
  189. }
  190. })(req, res, () => {});
  191. };
  192. /**
  193. * middleware that login with LocalStrategy
  194. * @param {*} req
  195. * @param {*} res
  196. * @param {*} next
  197. */
  198. const loginWithLocal = (req, res, next) => {
  199. if (!req.form.isValid) {
  200. return res.render('login', {
  201. });
  202. }
  203. passport.authenticate('local', (err, user, info) => {
  204. debug('--- authenticate with LocalStrategy ---');
  205. debug('user', user);
  206. debug('info', info);
  207. if (err) { // DB Error
  208. logger.error('Database Server Error: ', err);
  209. req.flash('warningMessage', 'Database Server Error occured.');
  210. return next(); // pass and the flash message is displayed when all of authentications are failed.
  211. }
  212. if (!user) { return next() }
  213. req.logIn(user, (err) => {
  214. if (err) { return next() }
  215. else {
  216. return loginSuccess(req, res, user);
  217. }
  218. });
  219. })(req, res, next);
  220. };
  221. return {
  222. loginFailure,
  223. loginWithLdap,
  224. testLdapCredentials,
  225. loginWithLocal,
  226. };
  227. };