Prevent banned user from trying to validate auth token multiple times. close #9798
This commit is contained in:
parent
6da65c3c84
commit
45f99fed8b
@ -9,6 +9,7 @@ import createNote from '/imports/api/note/server/methods/createNote';
|
|||||||
import createCaptions from '/imports/api/captions/server/methods/createCaptions';
|
import createCaptions from '/imports/api/captions/server/methods/createCaptions';
|
||||||
import { addAnnotationsStreamer } from '/imports/api/annotations/server/streamer';
|
import { addAnnotationsStreamer } from '/imports/api/annotations/server/streamer';
|
||||||
import { addCursorStreamer } from '/imports/api/cursor/server/streamer';
|
import { addCursorStreamer } from '/imports/api/cursor/server/streamer';
|
||||||
|
import BannedUsers from '/imports/api/users/server/store/bannedUsers';
|
||||||
|
|
||||||
export default function addMeeting(meeting) {
|
export default function addMeeting(meeting) {
|
||||||
const meetingId = meeting.meetingProp.intId;
|
const meetingId = meeting.meetingProp.intId;
|
||||||
@ -145,6 +146,7 @@ export default function addMeeting(meeting) {
|
|||||||
// better place we can run this post-creation routine?
|
// better place we can run this post-creation routine?
|
||||||
createNote(meetingId);
|
createNote(meetingId);
|
||||||
createCaptions(meetingId);
|
createCaptions(meetingId);
|
||||||
|
BannedUsers.init(meetingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numChanged) {
|
if (numChanged) {
|
||||||
|
@ -15,7 +15,12 @@ const clearOtherSessions = (sessionUserId, current = false) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function handleValidateAuthToken({ body }, meetingId) {
|
export default function handleValidateAuthToken({ body }, meetingId) {
|
||||||
const { userId, valid, authToken, waitForApproval } = body;
|
const {
|
||||||
|
userId,
|
||||||
|
valid,
|
||||||
|
authToken,
|
||||||
|
waitForApproval,
|
||||||
|
} = body;
|
||||||
|
|
||||||
check(userId, String);
|
check(userId, String);
|
||||||
check(authToken, String);
|
check(authToken, String);
|
||||||
@ -24,50 +29,50 @@ export default function handleValidateAuthToken({ body }, meetingId) {
|
|||||||
|
|
||||||
const pendingAuths = pendingAuthenticationsStore.take(meetingId, userId, authToken);
|
const pendingAuths = pendingAuthenticationsStore.take(meetingId, userId, authToken);
|
||||||
|
|
||||||
if(!valid) {
|
if (!valid) {
|
||||||
pendingAuths.forEach (
|
pendingAuths.forEach(
|
||||||
pendingAuth => {
|
(pendingAuth) => {
|
||||||
try {
|
try {
|
||||||
const {methodInvocationObject} = pendingAuth;
|
const { methodInvocationObject } = pendingAuth;
|
||||||
const connectionId = methodInvocationObject.connection.id;
|
const connectionId = methodInvocationObject.connection.id;
|
||||||
|
|
||||||
// Schedule socket disconnection for this user, giving some time for client receiving the reason of disconnection
|
// Schedule socket disconnection for this user, giving some time for client receiving the reason of disconnection
|
||||||
Meteor.setTimeout(()=>{
|
Meteor.setTimeout(() => {
|
||||||
methodInvocationObject.connection.close();
|
methodInvocationObject.connection.close();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|
||||||
Logger.info(`Closed connection ${connectionId} due to invalid auth token.`);
|
Logger.info(`Closed connection ${connectionId} due to invalid auth token.`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.error(`Error closing socket for meetingId '${meetingId}', userId '${userId}', authToken ${authToken}`);
|
Logger.error(`Error closing socket for meetingId '${meetingId}', userId '${userId}', authToken ${authToken}`);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(valid) {
|
if (valid) {
|
||||||
// Define user ID on connections
|
// Define user ID on connections
|
||||||
pendingAuths.forEach (
|
pendingAuths.forEach(
|
||||||
pendingAuth => {
|
(pendingAuth) => {
|
||||||
const {methodInvocationObject} = pendingAuth;
|
const { methodInvocationObject } = pendingAuth;
|
||||||
|
|
||||||
/* Logic migrated from validateAuthToken method ( postponed to only run in case of success response ) - Begin */
|
/* Logic migrated from validateAuthToken method ( postponed to only run in case of success response ) - Begin */
|
||||||
const sessionId = `${meetingId}--${userId}`;
|
const sessionId = `${meetingId}--${userId}`;
|
||||||
methodInvocationObject.setUserId(sessionId);
|
methodInvocationObject.setUserId(sessionId);
|
||||||
|
|
||||||
const User = Users.findOne({
|
const User = Users.findOne({
|
||||||
meetingId,
|
meetingId,
|
||||||
userId: userId,
|
userId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!User) {
|
if (!User) {
|
||||||
createDummyUser(meetingId, userId, authToken);
|
createDummyUser(meetingId, userId, authToken);
|
||||||
}
|
|
||||||
|
|
||||||
setConnectionIdAndAuthToken(meetingId, userId, methodInvocationObject.connection.id, authToken);
|
|
||||||
/* End of logic migrated from validateAuthToken */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setConnectionIdAndAuthToken(meetingId, userId, methodInvocationObject.connection.id, authToken);
|
||||||
|
/* End of logic migrated from validateAuthToken */
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,4 +121,4 @@ export default function handleValidateAuthToken({ body }, meetingId) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Users.update(selector, modifier, cb);
|
Users.update(selector, modifier, cb);
|
||||||
}
|
}
|
@ -2,6 +2,8 @@ import { Meteor } from 'meteor/meteor';
|
|||||||
import { check } from 'meteor/check';
|
import { check } from 'meteor/check';
|
||||||
import RedisPubSub from '/imports/startup/server/redis';
|
import RedisPubSub from '/imports/startup/server/redis';
|
||||||
import { extractCredentials } from '/imports/api/common/server/helpers';
|
import { extractCredentials } from '/imports/api/common/server/helpers';
|
||||||
|
import Users from '/imports/api/users';
|
||||||
|
import BannedUsers from '/imports/api/users/server/store/bannedUsers';
|
||||||
|
|
||||||
export default function removeUser(userId, banUser) {
|
export default function removeUser(userId, banUser) {
|
||||||
const REDIS_CONFIG = Meteor.settings.private.redis;
|
const REDIS_CONFIG = Meteor.settings.private.redis;
|
||||||
@ -18,5 +20,9 @@ export default function removeUser(userId, banUser) {
|
|||||||
banUser,
|
banUser,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const removedUser = Users.findOne({ meetingId, userId }, { extId: 1 });
|
||||||
|
|
||||||
|
if (banUser && removedUser) BannedUsers.add(meetingId, removedUser.extId);
|
||||||
|
|
||||||
return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, ejectedBy, payload);
|
return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, ejectedBy, payload);
|
||||||
}
|
}
|
@ -2,12 +2,16 @@ import { Meteor } from 'meteor/meteor';
|
|||||||
import RedisPubSub from '/imports/startup/server/redis';
|
import RedisPubSub from '/imports/startup/server/redis';
|
||||||
import Logger from '/imports/startup/server/logger';
|
import Logger from '/imports/startup/server/logger';
|
||||||
import pendingAuthenticationsStore from '../store/pendingAuthentications';
|
import pendingAuthenticationsStore from '../store/pendingAuthentications';
|
||||||
|
import BannedUsers from '../store/bannedUsers';
|
||||||
|
|
||||||
export default function validateAuthToken(meetingId, requesterUserId, requesterToken) {
|
export default function validateAuthToken(meetingId, requesterUserId, requesterToken, externalId) {
|
||||||
const REDIS_CONFIG = Meteor.settings.private.redis;
|
const REDIS_CONFIG = Meteor.settings.private.redis;
|
||||||
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
|
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
|
||||||
const EVENT_NAME = 'ValidateAuthTokenReqMsg';
|
const EVENT_NAME = 'ValidateAuthTokenReqMsg';
|
||||||
|
|
||||||
|
// Check if externalId is banned from the meeting
|
||||||
|
if (externalId && BannedUsers.has(meetingId, externalId)) return;
|
||||||
|
|
||||||
// Store reference of methodInvocationObject ( to postpone the connection userId definition )
|
// Store reference of methodInvocationObject ( to postpone the connection userId definition )
|
||||||
pendingAuthenticationsStore.add(meetingId, requesterUserId, requesterToken, this);
|
pendingAuthenticationsStore.add(meetingId, requesterUserId, requesterToken, this);
|
||||||
|
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
import Logger from '/imports/startup/server/logger';
|
||||||
|
|
||||||
|
class BannedUsers {
|
||||||
|
constructor() {
|
||||||
|
Logger.debug('BannedUsers :: Initializing');
|
||||||
|
this.store = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
init(meetingId) {
|
||||||
|
Logger.debug('BannedUsers :: init', meetingId);
|
||||||
|
|
||||||
|
if (!this.store[meetingId]) this.store[meetingId] = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
add(meetingId, externalId) {
|
||||||
|
Logger.debug('BannedUsers :: add', { meetingId, externalId });
|
||||||
|
if (!this.store[meetingId]) this.store[meetingId] = new Set();
|
||||||
|
|
||||||
|
this.store[meetingId].add(externalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(meetingId) {
|
||||||
|
Logger.debug('BannedUsers :: delete', meetingId);
|
||||||
|
delete this.store[meetingId];
|
||||||
|
}
|
||||||
|
|
||||||
|
has(meetingId, externalId) {
|
||||||
|
Logger.debug('BannedUsers :: has', { meetingId, externalId });
|
||||||
|
if (!this.store[meetingId]) this.store[meetingId] = new Set();
|
||||||
|
|
||||||
|
return this.store[meetingId].has(externalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new BannedUsers();
|
@ -207,7 +207,7 @@ class Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validateAuthToken() {
|
validateAuthToken() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
let computation = null;
|
let computation = null;
|
||||||
|
|
||||||
const validationTimeout = setTimeout(() => {
|
const validationTimeout = setTimeout(() => {
|
||||||
@ -218,9 +218,18 @@ class Auth {
|
|||||||
});
|
});
|
||||||
}, CONNECTION_TIMEOUT);
|
}, CONNECTION_TIMEOUT);
|
||||||
|
|
||||||
|
const result = await makeCall('validateAuthToken', this.meetingID, this.userID, this.token, this.externUserID);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
reject({
|
||||||
|
error: 401,
|
||||||
|
description: 'User has been banned.',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Tracker.autorun((c) => {
|
Tracker.autorun((c) => {
|
||||||
computation = c;
|
computation = c;
|
||||||
makeCall('validateAuthToken', this.meetingID, this.userID, this.token);
|
|
||||||
Meteor.subscribe('current-user');
|
Meteor.subscribe('current-user');
|
||||||
|
|
||||||
const selector = { meetingId: this.meetingID, userId: this.userID };
|
const selector = { meetingId: this.meetingID, userId: this.userID };
|
||||||
@ -237,6 +246,7 @@ class Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (User.ejected) {
|
if (User.ejected) {
|
||||||
|
computation.stop();
|
||||||
reject({
|
reject({
|
||||||
error: 401,
|
error: 401,
|
||||||
description: 'User has been ejected.',
|
description: 'User has been ejected.',
|
||||||
@ -271,4 +281,4 @@ class Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AuthSingleton = new Auth();
|
const AuthSingleton = new Auth();
|
||||||
export default AuthSingleton;
|
export default AuthSingleton;
|
Loading…
Reference in New Issue
Block a user