loginHandler.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. import {slug, getLdapUsername, getLdapEmail, getLdapUserUniqueID, syncUserData, addLdapUser} from './sync';
  2. import LDAP from './ldap';
  3. import { log_debug, log_info, log_warn, log_error } from './logger';
  4. function fallbackDefaultAccountSystem(bind, username, password) {
  5. if (typeof username === 'string') {
  6. if (username.indexOf('@') === -1) {
  7. username = {username};
  8. } else {
  9. username = {email: username};
  10. }
  11. }
  12. log_info('Fallback to default account system: ', username );
  13. const loginRequest = {
  14. user: username,
  15. password: {
  16. digest: SHA256(password),
  17. algorithm: 'sha-256',
  18. },
  19. };
  20. log_debug('Fallback options: ', loginRequest);
  21. return Accounts._runLoginHandlers(bind, loginRequest);
  22. }
  23. Accounts.registerLoginHandler('ldap', function(loginRequest) {
  24. if (!loginRequest.ldap || !loginRequest.ldapOptions) {
  25. return undefined;
  26. }
  27. log_info('Init LDAP login', loginRequest.username);
  28. if (LDAP.settings_get('LDAP_ENABLE') !== true) {
  29. return fallbackDefaultAccountSystem(this, loginRequest.username, loginRequest.ldapPass);
  30. }
  31. const self = this;
  32. const ldap = new LDAP();
  33. let ldapUser;
  34. try {
  35. ldap.connectSync();
  36. if (!!LDAP.settings_get('LDAP_USER_AUTHENTICATION')) {
  37. ldap.bindUserIfNecessary(loginRequest.username, loginRequest.ldapPass);
  38. ldapUser = ldap.searchUsersSync(loginRequest.username)[0];
  39. } else {
  40. const users = ldap.searchUsersSync(loginRequest.username);
  41. if (users.length !== 1) {
  42. log_info('Search returned', users.length, 'record(s) for', loginRequest.username);
  43. throw new Error('User not Found');
  44. }
  45. if (ldap.isUserInGroup(loginRequest.username, users[0])) {
  46. ldapUser = users[0];
  47. } else {
  48. throw new Error('User not in a valid group');
  49. }
  50. if (ldap.authSync(users[0].dn, loginRequest.ldapPass) !== true) {
  51. ldapUser = null;
  52. log_info('Wrong password for', loginRequest.username)
  53. }
  54. }
  55. } catch (error) {
  56. log_error(error);
  57. }
  58. if (!ldapUser) {
  59. if (LDAP.settings_get('LDAP_LOGIN_FALLBACK') === true) {
  60. return fallbackDefaultAccountSystem(self, loginRequest.username, loginRequest.ldapPass);
  61. }
  62. throw new Meteor.Error('LDAP-login-error', `LDAP Authentication failed with provided username [${ loginRequest.username }]`);
  63. }
  64. // Look to see if user already exists
  65. let userQuery;
  66. const Unique_Identifier_Field = getLdapUserUniqueID(ldapUser);
  67. let user;
  68. // Attempt to find user by unique identifier
  69. if (Unique_Identifier_Field) {
  70. userQuery = {
  71. 'services.ldap.id': Unique_Identifier_Field.value,
  72. };
  73. log_info('Querying user');
  74. log_debug('userQuery', userQuery);
  75. user = Meteor.users.findOne(userQuery);
  76. }
  77. // Attempt to find user by username
  78. let username;
  79. let email;
  80. if (LDAP.settings_get('LDAP_USERNAME_FIELD') !== '') {
  81. username = slug(getLdapUsername(ldapUser));
  82. } else {
  83. username = slug(loginRequest.username);
  84. }
  85. if(LDAP.settings_get('LDAP_EMAIL_FIELD') !== '') {
  86. email = getLdapEmail(ldapUser);
  87. }
  88. if (!user) {
  89. if(email && LDAP.settings_get('LDAP_EMAIL_MATCH_REQUIRE') === true) {
  90. if(LDAP.settings_get('LDAP_EMAIL_MATCH_VERIFIED') === true) {
  91. userQuery = {
  92. '_id' : username,
  93. 'emails.0.address' : email,
  94. 'emails.0.verified' : true
  95. };
  96. } else {
  97. userQuery = {
  98. '_id' : username,
  99. 'emails.0.address' : email
  100. };
  101. }
  102. } else {
  103. userQuery = {
  104. username
  105. };
  106. }
  107. log_debug('userQuery', userQuery);
  108. user = Meteor.users.findOne(userQuery);
  109. }
  110. // Attempt to find user by e-mail address only
  111. if (!user && email && LDAP.settings_get('LDAP_EMAIL_MATCH_ENABLE') === true) {
  112. log_info('No user exists with username', username, '- attempting to find by e-mail address instead');
  113. if(LDAP.settings_get('LDAP_EMAIL_MATCH_VERIFIED') === true) {
  114. userQuery = {
  115. 'emails.0.address': email,
  116. 'emails.0.verified' : true
  117. };
  118. } else {
  119. userQuery = {
  120. 'emails.0.address' : email
  121. };
  122. }
  123. log_debug('userQuery', userQuery);
  124. user = Meteor.users.findOne(userQuery);
  125. }
  126. // Login user if they exist
  127. if (user) {
  128. if (user.authenticationMethod !== 'ldap' && LDAP.settings_get('LDAP_MERGE_EXISTING_USERS') !== true) {
  129. log_info('User exists without "authenticationMethod : ldap"');
  130. throw new Meteor.Error('LDAP-login-error', `LDAP Authentication succeded, but there's already a matching Wekan account in MongoDB`);
  131. }
  132. log_info('Logging user');
  133. const stampedToken = Accounts._generateStampedLoginToken();
  134. const update_data = {
  135. $push: {
  136. 'services.resume.loginTokens': Accounts._hashStampedToken(stampedToken),
  137. },
  138. };
  139. if (LDAP.settings_get('LDAP_SYNC_ADMIN_STATUS') === true) {
  140. log_debug('Updating admin status');
  141. const targetGroups = LDAP.settings_get('LDAP_SYNC_ADMIN_GROUPS').split(',');
  142. const groups = ldap.getUserGroups(username, ldapUser).filter((value) => targetGroups.includes(value));
  143. user.isAdmin = groups.length > 0;
  144. Meteor.users.update({_id: user._id}, {$set: {isAdmin: user.isAdmin}});
  145. }
  146. if( LDAP.settings_get('LDAP_SYNC_GROUP_ROLES') === true ) {
  147. log_debug('Updating Groups/Roles');
  148. const groups = ldap.getUserGroups(username, ldapUser);
  149. if( groups.length > 0 ) {
  150. Roles.setUserRoles(user._id, groups );
  151. log_info(`Updated roles to:${ groups.join(',')}`);
  152. }
  153. }
  154. Meteor.users.update(user._id, update_data );
  155. syncUserData(user, ldapUser);
  156. if (LDAP.settings_get('LDAP_LOGIN_FALLBACK') === true) {
  157. Accounts.setPassword(user._id, loginRequest.ldapPass, {logout: false});
  158. }
  159. return {
  160. userId: user._id,
  161. token: stampedToken.token,
  162. };
  163. }
  164. // Create new user
  165. log_info('User does not exist, creating', username);
  166. if (LDAP.settings_get('LDAP_USERNAME_FIELD') === '') {
  167. username = undefined;
  168. }
  169. if (LDAP.settings_get('LDAP_LOGIN_FALLBACK') !== true) {
  170. loginRequest.ldapPass = undefined;
  171. }
  172. const result = addLdapUser(ldapUser, username, loginRequest.ldapPass);
  173. if (LDAP.settings_get('LDAP_SYNC_ADMIN_STATUS') === true) {
  174. log_debug('Updating admin status');
  175. const targetGroups = LDAP.settings_get('LDAP_SYNC_ADMIN_GROUPS').split(',');
  176. const groups = ldap.getUserGroups(username, ldapUser).filter((value) => targetGroups.includes(value));
  177. result.isAdmin = groups.length > 0;
  178. Meteor.users.update({_id: result.userId}, {$set: {isAdmin: result.isAdmin}});
  179. }
  180. if( LDAP.settings_get('LDAP_SYNC_GROUP_ROLES') === true ) {
  181. const groups = ldap.getUserGroups(username, ldapUser);
  182. if( groups.length > 0 ) {
  183. Roles.setUserRoles(result.userId, groups );
  184. log_info(`Set roles to:${ groups.join(',')}`);
  185. }
  186. }
  187. if (result instanceof Error) {
  188. throw result;
  189. }
  190. return result;
  191. });