Guest User

Untitled

a guest
Aug 6th, 2018
138
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.45 KB | None | 0 0
  1. /* globals:CROWD:true */
  2. /* eslint new-cap: [2, {"capIsNewExceptions": ["SHA256"]}] */
  3. const logger = new Logger('CROWD', {});
  4.  
  5. function fallbackDefaultAccountSystem(bind, username, password) {
  6. if (typeof username === 'string') {
  7. if (username.indexOf('@') === -1) {
  8. username = {username};
  9. } else {
  10. username = {email: username};
  11. }
  12. }
  13.  
  14. logger.info('Fallback to default account system', username);
  15.  
  16. const loginRequest = {
  17. user: username,
  18. password: {
  19. digest: SHA256(password),
  20. algorithm: 'sha-256'
  21. }
  22. };
  23.  
  24. return Accounts._runLoginHandlers(bind, loginRequest);
  25. }
  26.  
  27. const CROWD = class CROWD {
  28. constructor() {
  29. const AtlassianCrowd = require('atlassian-crowd');
  30. let url = RocketChat.settings.get('CROWD_URL');
  31.  
  32. this.options = {
  33. crowd: {
  34. base: (!/\/$/.test(url) ? url += '/' : url)
  35. },
  36. application: {
  37. name: RocketChat.settings.get('CROWD_APP_USERNAME'),
  38. password: RocketChat.settings.get('CROWD_APP_PASSWORD')
  39. },
  40. rejectUnauthorized: RocketChat.settings.get('CROWD_Reject_Unauthorized')
  41. };
  42.  
  43. this.crowdClient = new AtlassianCrowd(this.options);
  44.  
  45. this.crowdClient.user.authenticateSync = Meteor.wrapAsync(this.crowdClient.user.authenticate, this);
  46. this.crowdClient.user.findSync = Meteor.wrapAsync(this.crowdClient.user.find, this);
  47. this.crowdClient.searchSync = Meteor.wrapAsync(this.crowdClient.search, this);
  48. this.crowdClient.pingSync = Meteor.wrapAsync(this.crowdClient.ping, this);
  49. }
  50.  
  51. checkConnection() {
  52. this.crowdClient.pingSync();
  53. }
  54.  
  55. fetchCrowdUser(username) {
  56. const userResponse = this.crowdClient.user.findSync(username);
  57.  
  58. return {
  59. displayname: userResponse['display-name'],
  60. username: userResponse.name,
  61. email: userResponse.email,
  62. active: userResponse.active
  63. };
  64. }
  65.  
  66. authenticate(username, password) {
  67. if (!username || !password) {
  68. logger.error('No username or password');
  69. return;
  70. }
  71.  
  72. logger.info('Going to crowd:', username);
  73.  
  74. if (this.crowdClient.user.authenticateSync(username, password)) {
  75.  
  76. const crowdUser = this.fetchCrowdUser(username);
  77.  
  78. crowdUser.password = password;
  79.  
  80. return crowdUser;
  81. }
  82. }
  83.  
  84. syncDataToUser(crowdUser, id) {
  85. const user = {
  86. crowd_username: crowdUser.username,
  87. emails: [{
  88. address : crowdUser.email,
  89. verified: true
  90. }],
  91. active: crowdUser.active,
  92. crowd: true
  93. };
  94.  
  95. if (crowdUser.password) {
  96. // this sets the password so it is encrypted and can be used
  97. // if Crowd is offline
  98. Accounts.setPassword(id, crowdUser.password, {
  99. logout: false
  100. });
  101. }
  102.  
  103. if (crowdUser.displayname) {
  104. RocketChat._setRealName(id, crowdUser.displayname);
  105. }
  106.  
  107. Meteor.users.update(id, {
  108. $set: user
  109. });
  110. }
  111.  
  112. sync() {
  113. // if crowd is disabled bail out
  114. if (RocketChat.settings.get('CROWD_Enable') !== true) {
  115. return;
  116. }
  117.  
  118. return new Promise((resolve, reject) => {
  119.  
  120. const self = this;
  121. const users = RocketChat.models.Users.findCrowdUsers() || [];
  122.  
  123. logger.info('Sync started...');
  124.  
  125. users.forEach(function(user) {
  126.  
  127. let username = user.hasOwnProperty('crowd_username') ? user.crowd_username : user.username;
  128.  
  129. try {
  130. logger.info('Syncing user', username);
  131.  
  132. const crowdUser = self.fetchCrowdUser(username);
  133.  
  134. self.syncDataToUser(crowdUser, user._id);
  135. resolve();
  136. } catch (error) {
  137. logger.debug(error);
  138. logger.error('Could not sync user with username', username);
  139.  
  140. const email = user.emails[0].address;
  141. logger.info('Attempting to find for user by email', email);
  142.  
  143. const response = self.crowdClient.searchSync('user', `email=" ${ email } "`);
  144. if (response.users && response.users.length === 1) {
  145. username = response.users[0].name;
  146. logger.info('User found. Syncing user', username);
  147.  
  148. const crowdUser = self.fetchCrowdUser(response.users[0].name);
  149.  
  150. self.syncDataToUser(crowdUser, user._id);
  151. resolve();
  152. } else {
  153. reject();
  154. throw new Error('User does not exist or email is not unique');
  155. }
  156. }
  157. });
  158. });
  159. }
  160.  
  161. // TODO: This should use the built in mechanism for creating usernames
  162. // Crowd will allow emails as usernames which doesn't work very well in Rocket Chat
  163. // This mechanism works for a very limited set of use cases
  164. cleanUsername(username) {
  165. if (RocketChat.settings.get('CROWD_CLEAN_USERNAMES') === true) {
  166. return username.split('@')[0].toLowerCase();
  167. }
  168. return username;
  169. }
  170.  
  171. updateUserCollection(crowdUser) {
  172. const userQuery = {
  173. crowd: true,
  174. username: crowdUser.username
  175. };
  176.  
  177. // find our existing user if they exist
  178. const user = Meteor.users.findOne(userQuery);
  179.  
  180. if (user) {
  181. const stampedToken = Accounts._generateStampedLoginToken();
  182.  
  183. Meteor.users.update(user._id, {
  184. $push: {
  185. 'services.resume.loginTokens': Accounts._hashStampedToken(stampedToken)
  186. }
  187. });
  188.  
  189. this.syncDataToUser(crowdUser, user._id);
  190.  
  191. return {
  192. userId: user._id,
  193. token: stampedToken.token
  194. };
  195. }
  196.  
  197. // Attempt to create the new user
  198. try {
  199. // set a username that works with RocketChat
  200. crowdUser.username = this.cleanUsername(crowdUser.username);
  201.  
  202. // create the user
  203. crowdUser._id = Accounts.createUser(crowdUser);
  204.  
  205. // sync the user data
  206. this.syncDataToUser(crowdUser, crowdUser._id);
  207.  
  208. return {
  209. userId: crowdUser._id
  210. };
  211. } catch (error) {
  212. logger.error('Error creating new crowd user.', error.message);
  213. }
  214. }
  215. };
  216.  
  217. Accounts.registerLoginHandler('crowd', function(loginRequest) {
  218. if (loginRequest.crowd) {
  219. logger.info('Init CROWD login', loginRequest.username);
  220.  
  221. if (RocketChat.settings.get('CROWD_Enable') !== true) {
  222. return fallbackDefaultAccountSystem(this, loginRequest.username, loginRequest.crowdPassword);
  223. }
  224.  
  225. try {
  226. const crowd = new CROWD();
  227. const user = crowd.authenticate(loginRequest.username, loginRequest.crowdPassword);
  228.  
  229. return crowd.updateUserCollection(user);
  230. } catch (error) {
  231. logger.debug(error);
  232. logger.error('Crowd user not authenticated due to an error, falling back');
  233. return fallbackDefaultAccountSystem(this, loginRequest.username, loginRequest.crowdPassword);
  234. }
  235. }
  236. return undefined;
  237. });
  238.  
  239. let interval;
  240. let timeout;
  241.  
  242. RocketChat.settings.get('CROWD_SYNC_INTERVAL', function(key, value) {
  243. Meteor.clearInterval(interval);
  244.  
  245. logger.info('Setting CROWD sync interval to', value, 'minutes');
  246.  
  247. const crowd = new CROWD();
  248. interval = Meteor.setInterval(function() {
  249. crowd.sync();
  250. }, value * 60 * 1000);
  251. });
  252.  
  253. RocketChat.settings.get('CROWD_Sync_User_Data', function(key, value) {
  254. Meteor.clearInterval(interval);
  255. Meteor.clearTimeout(timeout);
  256.  
  257. if (value === true) {
  258. const crowd = new CROWD();
  259. const syncInterval = RocketChat.settings.get('CROWD_SYNC_INTERVAL');
  260.  
  261. logger.info('Enabling CROWD user sync');
  262.  
  263. interval = Meteor.setInterval(function() {
  264. crowd.sync();
  265. }, syncInterval * 60 * 1000);
  266.  
  267. timeout = Meteor.setTimeout(function() {
  268. crowd.sync();
  269. }, 1000 * 30);
  270. } else {
  271. logger.info('Disabling CROWD user sync');
  272. }
  273. });
  274.  
  275. Meteor.methods({
  276. crowd_test_connection() {
  277. const user = Meteor.user();
  278. if (!user) {
  279. throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'crowd_test_connection' });
  280. }
  281.  
  282. if (!RocketChat.authz.hasRole(user._id, 'admin')) {
  283. throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'crowd_test_connection' });
  284. }
  285.  
  286. if (RocketChat.settings.get('CROWD_Enable') !== true) {
  287. throw new Meteor.Error('crowd_disabled');
  288. }
  289.  
  290. try {
  291. const crowd = new CROWD();
  292. crowd.checkConnection();
  293.  
  294. return {
  295. message: 'Connection success',
  296. params: []
  297. };
  298. } catch (error) {
  299. logger.error('Invalid crowd connection details, check the url and application username/password and make sure this server is allowed to speak to crowd');
  300. throw new Meteor.Error('Invalid connection details', '', { method: 'crowd_test_connection' });
  301. }
  302. },
  303. crowd_sync_users() {
  304. const user = Meteor.user();
  305. if (RocketChat.settings.get('CROWD_Enable') !== true) {
  306. throw new Meteor.Error('crowd_disabled');
  307. }
  308.  
  309. if (!RocketChat.authz.hasRole(user._id, 'admin')) {
  310. throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'crowd_sync_users' });
  311. }
  312.  
  313. try {
  314. const crowd = new CROWD();
  315. const startTime = (new Date()).valueOf();
  316. Promise.await(crowd.sync());
  317. const stopeTime = (new Date()).valueOf();
  318. const actual = Math.ceil((stopeTime - startTime) / 1000);
  319.  
  320. return {
  321. message: `User data synced in ${ actual } seconds`,
  322. params: []
  323. };
  324. } catch (error) {
  325. logger.error('Error syncing user data. ', error.message);
  326. throw new Meteor.Error('Error syncing user data', '', { method: 'crowd_sync_users' });
  327. }
  328. }
  329. });
Add Comment
Please, Sign In to add comment