diff --git a/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js b/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js index 42dd7e2e7f..c9637bdd74 100644 --- a/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js +++ b/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js @@ -4,6 +4,7 @@ import Users from '/imports/api/users'; import userJoin from './userJoin'; import pendingAuthenticationsStore from '../store/pendingAuthentications'; import createDummyUser from '../modifiers/createDummyUser'; +import ClientConnections from '/imports/startup/server/ClientConnections'; import upsertValidationState from '/imports/api/auth-token-validation/server/modifiers/upsertValidationState'; import { ValidationStates } from '/imports/api/auth-token-validation'; @@ -75,6 +76,7 @@ export default function handleValidateAuthToken({ body }, meetingId) { createDummyUser(meetingId, userId, authToken); } + ClientConnections.add(sessionId, methodInvocationObject.connection); upsertValidationState(meetingId, userId, ValidationStates.VALIDATED, methodInvocationObject.connection.id); /* End of logic migrated from validateAuthToken */ diff --git a/bigbluebutton-html5/imports/api/users/server/methods/validateAuthToken.js b/bigbluebutton-html5/imports/api/users/server/methods/validateAuthToken.js index a53690907a..e3ca329a3a 100644 --- a/bigbluebutton-html5/imports/api/users/server/methods/validateAuthToken.js +++ b/bigbluebutton-html5/imports/api/users/server/methods/validateAuthToken.js @@ -1,18 +1,18 @@ import { Meteor } from 'meteor/meteor'; import RedisPubSub from '/imports/startup/server/redis'; import Logger from '/imports/startup/server/logger'; -import ClientConnections from '/imports/startup/server/ClientConnections'; import upsertValidationState from '/imports/api/auth-token-validation/server/modifiers/upsertValidationState'; import { ValidationStates } from '/imports/api/auth-token-validation'; import pendingAuthenticationsStore from '../store/pendingAuthentications'; import BannedUsers from '../store/bannedUsers'; -import Users from '/imports/api/users'; export default function validateAuthToken(meetingId, requesterUserId, requesterToken, externalId) { const REDIS_CONFIG = Meteor.settings.private.redis; const CHANNEL = REDIS_CONFIG.channels.toAkkaApps; const EVENT_NAME = 'ValidateAuthTokenReqMsg'; + Logger.debug('ValidateAuthToken method called', { meetingId, requesterUserId, requesterToken, externalId }); + // Check if externalId is banned from the meeting if (externalId) { if (BannedUsers.has(meetingId, externalId)) { @@ -21,24 +21,7 @@ export default function validateAuthToken(meetingId, requesterUserId, requesterT } } - // Prevent users who have left or been ejected to use the same sessionToken again. - const isUserInvalid = Users.findOne({ - meetingId, - userId: requesterUserId, - authToken: requesterToken, - $or: [{ ejected: true }, { loggedOut: true }], - }); - - if (isUserInvalid) { - Logger.warn(`An invalid sessionToken tried to validateAuthToken meetingId=${meetingId} authToken=${requesterToken}`); - return { - invalid: true, - reason: `User has an invalid sessionToken due to ${isUserInvalid.ejected ? 'ejection' : 'log out'}`, - error_type: `invalid_session_token_due_to_${isUserInvalid.ejected ? 'eject' : 'log_out'}`, - }; - } - - ClientConnections.add(`${meetingId}--${requesterUserId}`, this.connection); + if (!meetingId) return false; // Store reference of methodInvocationObject ( to postpone the connection userId definition ) pendingAuthenticationsStore.add(meetingId, requesterUserId, requesterToken, this); diff --git a/bigbluebutton-html5/imports/startup/server/ClientConnections.js b/bigbluebutton-html5/imports/startup/server/ClientConnections.js index 5e4ca6bdfa..9bb837f4ae 100644 --- a/bigbluebutton-html5/imports/startup/server/ClientConnections.js +++ b/bigbluebutton-html5/imports/startup/server/ClientConnections.js @@ -1,6 +1,10 @@ import Logger from './logger'; import userLeaving from '/imports/api/users/server/methods/userLeaving'; import { extractCredentials } from '/imports/api/common/server/helpers'; +import AuthTokenValidation from '/imports/api/auth-token-validation'; +import Users from '/imports/api/users'; + +const { enabled, syncInterval } = Meteor.settings.public.syncUsersWithConnectionManager; class ClientConnections { constructor() { @@ -11,10 +15,15 @@ class ClientConnections { this.print(); }, 30000); - // setTimeout(() => { - // this.syncConnectionsWithServer(); - // }, 10000); + if (enabled) { + const syncConnections = Meteor.bindEnvironment(() => { + this.syncConnectionsWithServer(); + }); + setInterval(() => { + syncConnections(); + }, syncInterval); + } } add(sessionId, connection) { @@ -29,6 +38,13 @@ class ClientConnections { const { meetingId, requesterUserId: userId } = extractCredentials(sessionId); + if (!meetingId) { + Logger.error('Error on add new client connection. sessionId=${sessionId} connection=${connection.id}', + { logCode: 'client_connections_add_error_meeting_id_null', extraInfo: { meetingId, userId } } + ); + return false; + } + if (!this.exists(meetingId)) { Logger.info(`Meeting not found in connections: meetingId=${meetingId}`); this.createMeetingConnections(meetingId); @@ -92,7 +108,7 @@ class ClientConnections { Logger.info(`Removing connectionId for user. sessionId=${sessionId} connectionId=${connectionId}`); const { meetingId, requesterUserId: userId } = extractCredentials(sessionId); - const meetingConnections = this.connections.get(meetingId) + const meetingConnections = this.connections.get(meetingId); if (meetingConnections?.has(userId)) { const filteredConnections = meetingConnections.get(userId).filter(c => c !== connectionId); @@ -109,7 +125,38 @@ class ClientConnections { } syncConnectionsWithServer() { - console.error('syncConnectionsWithServer', Array.from(Meteor.server.sessions.keys()), Meteor.server); + Logger.info('Syncing ClientConnections with server'); + const activeConnections = Array.from(Meteor.server.sessions.keys()); + + Logger.debug(`Found ${activeConnections.length} active connections in server`); + + const onlineUsers = AuthTokenValidation + .find( + { connectionId: { $in: activeConnections } }, + { fields: { meetingId: 1, userId: 1 } } + ) + .fetch(); + + const onlineUsersId = onlineUsers.map(({ userId }) => userId); + + const usersQuery = { userId: { $nin: onlineUsersId } }; + + const userWithoutConnectionIds = Users.find(usersQuery, { fields: { meetingId: 1, userId: 1 } }).fetch(); + + const removedUsersWithoutConnection = Users.remove(usersQuery); + + if (removedUsersWithoutConnection) { + Logger.info(`Removed ${removedUsersWithoutConnection} users that are not connected`); + Logger.info(`Clearing connections`); + try { + userWithoutConnectionIds + .forEach(({ meetingId, userId }) => { + this.removeClientConnection(`${meetingId}--${userId}`); + }); + } catch (err) { + Logger.error('Error on sync ClientConnections', err); + } + } } } diff --git a/bigbluebutton-html5/private/config/settings.yml b/bigbluebutton-html5/private/config/settings.yml index 886764a141..c80f93df1c 100755 --- a/bigbluebutton-html5/private/config/settings.yml +++ b/bigbluebutton-html5/private/config/settings.yml @@ -272,9 +272,9 @@ public: mobilePageSizes: moderator: 2 viewer: 2 - pingPong: - clearUsersInSeconds: 180 - pongTimeInSeconds: 15 + syncUsersWithConnectionManager: + enabled: false + syncInterval: 60000 allowOutsideCommands: toggleRecording: false toggleSelfVoice: false