Merge branch 'hotfix/21.56.9' into master
[gitter.git] / modules / rooms / lib / room-invite-service.js
blob0cc0f39fe64f8775e7e8891248ba4e711c41c941
1 'use strict';
3 var env = require('gitter-web-env');
4 var stats = env.stats;
5 const config = env.config;
6 const redisClient = env.redis.getClient();
7 var Promise = require('bluebird');
8 var isValidEmail = require('email-validator').validate;
9 var StatusError = require('statuserror');
10 const dolph = require('dolph');
11 var emailNotificationService = require('gitter-web-email-notifications');
12 var roomService = require('gitter-web-rooms');
13 var invitesService = require('gitter-web-invites/lib/invites-service');
15 /**
16  * @private
17  */
18 function addUserToRoomInsteadOfInvite(room, invitingUser, userToInvite) {
19   return roomService.addUserToRoom(room, invitingUser, userToInvite);
22 /**
23  * @private
24  */
25 function createInviteForNewUser(room, invitingUser, type, externalId, emailAddress) {
26   return Promise.try(function() {
27     // If an email address is provided, assert that its valid
28     // and use it
29     if (emailAddress) {
30       if (!isValidEmail(emailAddress)) {
31         throw new StatusError(400);
32       }
33       return emailAddress;
34     }
36     // No email address was provided, attempt to
37     // sniff out the email address given the external username and type
38     return invitesService.resolveEmailAddress(invitingUser, type, externalId);
39   })
40     .bind({
41       email: null
42     })
43     .then(function(resolvedEmailAddress) {
44       // The client needs to submit the request with an email address
45       if (!resolvedEmailAddress) throw new StatusError(428);
47       return invitesService.createInvite(room._id, {
48         type: type,
49         externalId: externalId,
50         emailAddress: resolvedEmailAddress,
51         invitedByUserId: invitingUser._id
52       });
53     })
54     .tap(function(invite) {
55       stats.event('new_invite', {
56         userId: invitingUser && (invitingUser.id || invitingUser._id),
57         troupeId: room && (room.id || room._id),
58         type: type,
59         uri: room && room.uri
60       });
62       return emailNotificationService.sendInvitation(invitingUser, invite, room);
63     })
64     .then(function(invite) {
65       return invite.emailAddress;
66     });
69 const ROOM_INVITE_RATE_LIMIT_THRESHOLD = config.get('email:inviteEmailAbuseThresholdPerDay') || 10;
70 const ROOM_INVITE_RATE_LIMIT_EXPIRY = 24 * 60 * 60;
71 const roomInviteRateLimiter = Promise.promisify(
72   dolph.rateLimiter({
73     prefix: 'ris:',
74     redisClient: redisClient
75   })
78 /**
79  * @return {
80  *           status: 'added'/'invited'
81  *           emailAddress: '...'        // When the user has been invited
82  *           user: '...'                // When the user was added
83  *         }
84  * @throws HTTP 428 (email address required)
85  */
86 async function createInvite(room, invitingUser, options) {
87   const type = options.type;
88   const externalId = options.externalId;
89   const emailAddress = options.emailAddress;
91   // Firstly, try figure out whether this user is already on gitter.
92   const userToInvite = await invitesService.findExistingUser(type, externalId);
94   if (userToInvite) {
95     // The user already exists!
96     // Rather than inviting them, we'll add them
97     // immediately (for now)
98     return addUserToRoomInsteadOfInvite(room, invitingUser, userToInvite).then(function() {
99       return {
100         status: 'added',
101         user: userToInvite
102       };
103     });
104   } else {
105     // See https://gitlab.com/gitterHQ/webapp/issues/2153
106     if (config.get('email:limitInviteEmails')) {
107       const count = await roomInviteRateLimiter(invitingUser.id, ROOM_INVITE_RATE_LIMIT_EXPIRY);
108       if (count > ROOM_INVITE_RATE_LIMIT_THRESHOLD) {
109         throw new StatusError(
110           501,
111           `Inviting a user by email is limited to ${ROOM_INVITE_RATE_LIMIT_THRESHOLD} per day, see #2153`
112         );
113       }
114     }
116     // The user doesn't exist. We'll try invite them
117     return createInviteForNewUser(room, invitingUser, type, externalId, emailAddress).then(function(
118       resolvedEmailAddress
119     ) {
120       return {
121         status: 'invited',
122         emailAddress: resolvedEmailAddress
123       };
124     });
125   }
128 module.exports = {
129   createInvite: Promise.method(createInvite)