From 3e7d88586edc12646f8f968ce28aa43e3dd6e0a7 Mon Sep 17 00:00:00 2001 From: Tainan Felipe Date: Thu, 28 Oct 2021 14:21:30 -0300 Subject: [PATCH] Enhances authToken validation to solely use Meteor.call --- .../server/handlers/validateAuthToken.js | 1 - .../users/server/methods/validateAuthToken.js | 89 ++++++++++++++----- .../server/store/pendingAuthentications.js | 1 - .../imports/ui/components/app/container.jsx | 5 +- .../authenticated-handler/component.jsx | 1 - .../imports/ui/services/auth/index.js | 43 ++++----- 6 files changed, 83 insertions(+), 57 deletions(-) diff --git a/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js b/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js index 64ffe9a605..b7d5be1005 100644 --- a/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js +++ b/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js @@ -37,7 +37,6 @@ export default function handleValidateAuthToken({ body }, meetingId) { check(reasonCode, String); const pendingAuths = pendingAuthenticationsStore.take(meetingId, userId, authToken); - Logger.info(`PendingAuths length [${pendingAuths.length}]`); if (pendingAuths.length === 0) return; diff --git a/bigbluebutton-html5/imports/api/users/server/methods/validateAuthToken.js b/bigbluebutton-html5/imports/api/users/server/methods/validateAuthToken.js index 914382cde4..41f12410a7 100644 --- a/bigbluebutton-html5/imports/api/users/server/methods/validateAuthToken.js +++ b/bigbluebutton-html5/imports/api/users/server/methods/validateAuthToken.js @@ -2,32 +2,77 @@ import { Meteor } from 'meteor/meteor'; import RedisPubSub from '/imports/startup/server/redis'; import Logger from '/imports/startup/server/logger'; import upsertValidationState from '/imports/api/auth-token-validation/server/modifiers/upsertValidationState'; -import { ValidationStates } from '/imports/api/auth-token-validation'; +import AuthTokenValidation, { ValidationStates } from '/imports/api/auth-token-validation'; import pendingAuthenticationsStore from '../store/pendingAuthentications'; -export default function validateAuthToken(meetingId, requesterUserId, requesterToken, externalId) { - try { - const REDIS_CONFIG = Meteor.settings.private.redis; - const CHANNEL = REDIS_CONFIG.channels.toAkkaApps; - const EVENT_NAME = 'ValidateAuthTokenReqMsg'; +const AUTH_TIMEOUT = 120000; - Logger.debug('ValidateAuthToken method called', { meetingId, requesterUserId, requesterToken, externalId }); - - if (!meetingId) return false; - - // Store reference of methodInvocationObject ( to postpone the connection userId definition ) - pendingAuthenticationsStore.add(meetingId, requesterUserId, requesterToken, this); - upsertValidationState(meetingId, requesterUserId, ValidationStates.VALIDATING, this.connection.id); - - const payload = { - userId: requesterUserId, - authToken: requesterToken, +async function validateAuthToken(meetingId, requesterUserId, requesterToken, externalId) { + let setTimeoutRef = null; + const userValidation = await new Promise((res, rej) => { + const observeFunc = (obj) => { + if (obj.validationStatus === ValidationStates.VALIDATED) { + clearTimeout(setTimeoutRef); + return res(obj); + } + if (obj.validationStatus === ValidationStates.INVALID) { + clearTimeout(setTimeoutRef); + return res(obj); + } }; + const authTokenValidationObserver = AuthTokenValidation.find({ + connectionId: this.connection.id, + }).observe({ + added: observeFunc, + changed: observeFunc, + }); - Logger.info(`User '${requesterUserId}' is trying to validate auth token for meeting '${meetingId}' from connection '${this.connection.id}'`); + setTimeoutRef = setTimeout(() => { + observeFunc.stop(); + rej(); + }, AUTH_TIMEOUT); - return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload); - } catch (err) { - Logger.error(`Exception while invoking method validateAuthToken ${err.stack}`); - } + try { + 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 }); + + if (!meetingId) return false; + + // Store reference of methodInvocationObject ( to postpone the connection userId definition ) + pendingAuthenticationsStore.add(meetingId, requesterUserId, requesterToken, this); + upsertValidationState( + meetingId, + requesterUserId, + ValidationStates.VALIDATING, + this.connection.id, + ); + + const payload = { + userId: requesterUserId, + authToken: requesterToken, + }; + + Logger.info(`User '${requesterUserId}' is trying to validate auth token for meeting '${meetingId}' from connection '${this.connection.id}'`); + + return RedisPubSub.publishUserMessage( + CHANNEL, + EVENT_NAME, + meetingId, + requesterUserId, + payload, + ); + } catch (err) { + const errMsg = `Exception while invoking method validateAuthToken ${err}`; + Logger.error(errMsg); + rej(errMsg); + clearTimeout(setTimeoutRef); + authTokenValidationObserver.stop(); + } + }); + return userValidation; } + +export default validateAuthToken; diff --git a/bigbluebutton-html5/imports/api/users/server/store/pendingAuthentications.js b/bigbluebutton-html5/imports/api/users/server/store/pendingAuthentications.js index 97b13cc714..ffc451ae5a 100644 --- a/bigbluebutton-html5/imports/api/users/server/store/pendingAuthentications.js +++ b/bigbluebutton-html5/imports/api/users/server/store/pendingAuthentications.js @@ -35,7 +35,6 @@ class PendingAuthentitcations { // find matches const matches = this.store.filter(e => e.key === key); - // remove matches (if any) if (matches.length) { this.store = this.store.filter(e => e.key !== key); diff --git a/bigbluebutton-html5/imports/ui/components/app/container.jsx b/bigbluebutton-html5/imports/ui/components/app/container.jsx index 1a10a375bf..3509d7e217 100755 --- a/bigbluebutton-html5/imports/ui/components/app/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/app/container.jsx @@ -3,7 +3,6 @@ import { withTracker } from 'meteor/react-meteor-data'; import { defineMessages, injectIntl } from 'react-intl'; import PropTypes from 'prop-types'; import Auth from '/imports/ui/services/auth'; -import AuthTokenValidation from '/imports/api/auth-token-validation'; import Users from '/imports/ui/local-collections/users-collection/users'; import Meetings from '/imports/ui/local-collections/meetings-collection/meetings'; import { notify } from '/imports/ui/services/notification'; @@ -120,9 +119,7 @@ const currentUserEmoji = (currentUser) => (currentUser ); export default injectIntl(withModalMounter(withTracker(({ intl, baseControls }) => { - const authTokenValidation = AuthTokenValidation.findOne({}, { sort: { updatedAt: -1 } }); - - if (authTokenValidation.connectionId !== Meteor.connection._lastSessionId) { + if (Auth.connectionID !== Meteor.connection._lastSessionId) { endMeeting('403'); } diff --git a/bigbluebutton-html5/imports/ui/components/authenticated-handler/component.jsx b/bigbluebutton-html5/imports/ui/components/authenticated-handler/component.jsx index 4bd72a2034..ce615f99ae 100644 --- a/bigbluebutton-html5/imports/ui/components/authenticated-handler/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/authenticated-handler/component.jsx @@ -94,5 +94,4 @@ class AuthenticatedHandler extends Component { } } - export default AuthenticatedHandler; diff --git a/bigbluebutton-html5/imports/ui/services/auth/index.js b/bigbluebutton-html5/imports/ui/services/auth/index.js index edde5347bd..62ac75b353 100755 --- a/bigbluebutton-html5/imports/ui/services/auth/index.js +++ b/bigbluebutton-html5/imports/ui/services/auth/index.js @@ -33,6 +33,7 @@ class Auth { this._confname = Storage.getItem('confname'); this._externUserID = Storage.getItem('externUserID'); this._fullname = Storage.getItem('fullname'); + this._connectionID = Storage.getItem('connectionID'); } get meetingID() { @@ -142,6 +143,15 @@ class Auth { }; } + set _connectionID(connectionId) { + this._connectionID = connectionId; + Storage.setItem('sessionToken', this._connectionID); + } + + get sessionToken() { + return this._sessionToken; + } + set( meetingId, requesterUserId, @@ -213,53 +223,30 @@ class Auth { } validateAuthToken() { - return new Promise(async (resolve, reject) => { - let computation = null; - + return new Promise((resolve, reject) => { + SubscriptionRegistry.createSubscription('current-user'); const validationTimeout = setTimeout(() => { - computation.stop(); reject({ error: 408, description: 'Authentication timeout', }); }, CONNECTION_TIMEOUT); - makeCall('validateAuthToken', this.meetingID, this.userID, this.token, this.externUserID); - - const authTokenSubscription = SubscriptionRegistry.createSubscription('auth-token-validation', {}, { meetingId: this.meetingID, userId: this.userID }); - SubscriptionRegistry.createSubscription('current-user'); - - Tracker.autorun((c) => { - computation = c; - - if (!authTokenSubscription.ready()) { - return; - } - - const selector = { - connectionId: Meteor.connection._lastSessionId, - }; - - const authenticationTokenValidation = AuthTokenValidation.findOne(selector); - + Meteor.call('validateAuthToken', this.meetingID, this.userID, this.token, this.externUserID, (err, result) => { + const authenticationTokenValidation = result; if (!authenticationTokenValidation) return; switch (authenticationTokenValidation.validationStatus) { case ValidationStates.INVALID: - c.stop(); reject({ error: 403, description: authenticationTokenValidation.reason }); break; case ValidationStates.VALIDATED: initCursorStreamListener(); initAnnotationsStreamListener(); - c.stop(); clearTimeout(validationTimeout); + this.connectionID = authenticationTokenValidation.connectionId; setTimeout(() => resolve(true), 100); break; - case ValidationStates.VALIDATING: - break; - case ValidationStates.NOT_VALIDATED: - break; default: } });