diff --git a/apps/meteor/app/2fa/server/code/EmailCheck.ts b/apps/meteor/app/2fa/server/code/EmailCheck.ts index b8fcd5e2859..debf989c985 100644 --- a/apps/meteor/app/2fa/server/code/EmailCheck.ts +++ b/apps/meteor/app/2fa/server/code/EmailCheck.ts @@ -3,10 +3,10 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { Accounts } from 'meteor/accounts-base'; import bcrypt from 'bcrypt'; import type { IUser } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; import * as Mailer from '../../../mailer/server/api'; -import { Users } from '../../../models/server'; import type { ICodeCheck, IProcessInvalidCodeResult } from './ICodeCheck'; export class EmailCheck implements ICodeCheck { @@ -64,7 +64,7 @@ ${t('If_you_didnt_try_to_login_in_your_account_please_ignore_this_email')} }); } - public verify(user: IUser, codeFromEmail: string): boolean { + public async verify(user: IUser, codeFromEmail: string): Promise { if (!this.isEnabled(user)) { return false; } @@ -76,42 +76,40 @@ ${t('If_you_didnt_try_to_login_in_your_account_please_ignore_this_email')} // Remove non digits codeFromEmail = codeFromEmail.replace(/([^\d])/g, ''); - Users.removeExpiredEmailCodesOfUserId(user._id); + await Users.removeExpiredEmailCodesOfUserId(user._id); - const valid = user.services.emailCode.find(({ code, expire }) => { + for await (const { code, expire } of user.services.emailCode) { if (expire < new Date()) { - return false; + continue; } - if (bcrypt.compareSync(codeFromEmail, code)) { - Users.removeEmailCodeByUserIdAndCode(user._id, code); + if (await bcrypt.compare(codeFromEmail, code)) { + await Users.removeEmailCodeByUserIdAndCode(user._id, code); return true; } + } - return false; - }); - - return !!valid; + return false; } - public sendEmailCode(user: IUser): void { + public async sendEmailCode(user: IUser): Promise { const emails = this.getUserVerifiedEmails(user); const random = Random._randomString(6, '0123456789'); - const encryptedRandom = bcrypt.hashSync(random, Accounts._bcryptRounds()); + const encryptedRandom = await bcrypt.hash(random, Accounts._bcryptRounds()); const expire = new Date(); const expirationInSeconds = parseInt(settings.get('Accounts_TwoFactorAuthentication_By_Email_Code_Expiration') as string, 10); expire.setSeconds(expire.getSeconds() + expirationInSeconds); - Users.addEmailCodeByUserId(user._id, encryptedRandom, expire); + await Users.addEmailCodeByUserId(user._id, encryptedRandom, expire); for (const address of emails) { this.send2FAEmail(address, random, user); } } - public processInvalidCode(user: IUser): IProcessInvalidCodeResult { - Users.removeExpiredEmailCodesOfUserId(user._id); + public async processInvalidCode(user: IUser): Promise { + await Users.removeExpiredEmailCodesOfUserId(user._id); // Generate new code if the there isn't any code with more than 5 minutes to expire const expireWithDelta = new Date(); @@ -131,7 +129,7 @@ ${t('If_you_didnt_try_to_login_in_your_account_please_ignore_this_email')} }; } - this.sendEmailCode(user); + await this.sendEmailCode(user); return { codeGenerated: true, diff --git a/apps/meteor/app/2fa/server/code/ICodeCheck.ts b/apps/meteor/app/2fa/server/code/ICodeCheck.ts index 9d3318c1f8a..f0e165735ce 100644 --- a/apps/meteor/app/2fa/server/code/ICodeCheck.ts +++ b/apps/meteor/app/2fa/server/code/ICodeCheck.ts @@ -12,7 +12,7 @@ export interface ICodeCheck { isEnabled(user: IUser, force?: boolean): boolean; - verify(user: IUser, code: string, force?: boolean): boolean; + verify(user: IUser, code: string, force?: boolean): Promise; - processInvalidCode(user: IUser): IProcessInvalidCodeResult; + processInvalidCode(user: IUser): Promise; } diff --git a/apps/meteor/app/2fa/server/code/PasswordCheckFallback.ts b/apps/meteor/app/2fa/server/code/PasswordCheckFallback.ts index 7094779e5e1..a45f19545ff 100644 --- a/apps/meteor/app/2fa/server/code/PasswordCheckFallback.ts +++ b/apps/meteor/app/2fa/server/code/PasswordCheckFallback.ts @@ -19,7 +19,7 @@ export class PasswordCheckFallback implements ICodeCheck { return false; } - public verify(user: IUser, code: string, force: boolean): boolean { + public async verify(user: IUser, code: string, force: boolean): Promise { if (!this.isEnabled(user, force)) { return false; } @@ -36,7 +36,7 @@ export class PasswordCheckFallback implements ICodeCheck { return true; } - public processInvalidCode(): IProcessInvalidCodeResult { + public async processInvalidCode(): Promise { return { codeGenerated: false, }; diff --git a/apps/meteor/app/2fa/server/code/TOTPCheck.ts b/apps/meteor/app/2fa/server/code/TOTPCheck.ts index 08016449c41..ecf5db9e1db 100644 --- a/apps/meteor/app/2fa/server/code/TOTPCheck.ts +++ b/apps/meteor/app/2fa/server/code/TOTPCheck.ts @@ -15,7 +15,7 @@ export class TOTPCheck implements ICodeCheck { return user.services?.totp?.enabled === true; } - public verify(user: IUser, code: string): boolean { + public async verify(user: IUser, code: string): Promise { if (!this.isEnabled(user)) { return false; } @@ -32,7 +32,7 @@ export class TOTPCheck implements ICodeCheck { }); } - public processInvalidCode(): IProcessInvalidCodeResult { + public async processInvalidCode(): Promise { // Nothing to do return { codeGenerated: false, diff --git a/apps/meteor/app/2fa/server/code/index.ts b/apps/meteor/app/2fa/server/code/index.ts index cad38731e9f..70ac3f67559 100644 --- a/apps/meteor/app/2fa/server/code/index.ts +++ b/apps/meteor/app/2fa/server/code/index.ts @@ -3,13 +3,13 @@ import crypto from 'crypto'; import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; import type { IUser, IMethodConnection } from '@rocket.chat/core-typings'; +import { Users } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; import { TOTPCheck } from './TOTPCheck'; import { EmailCheck } from './EmailCheck'; import { PasswordCheckFallback } from './PasswordCheckFallback'; import type { ICodeCheck } from './ICodeCheck'; -import { Users } from '../../../models/server'; export interface ITwoFactorOptions { disablePasswordFallback?: boolean; @@ -42,9 +42,9 @@ function getAvailableMethodNames(user: IUser): string[] | [] { ); } -export function getUserForCheck(userId: string): IUser { +export async function getUserForCheck(userId: string): Promise { return Users.findOneById(userId, { - fields: { + projection: { 'emails': 1, 'language': 1, 'createdAt': 1, @@ -121,7 +121,7 @@ function isAuthorizedForToken(connection: IMethodConnection, user: IUser, option return true; } -function rememberAuthorization(connection: IMethodConnection, user: IUser): void { +async function rememberAuthorization(connection: IMethodConnection, user: IUser): Promise { const currentToken = Accounts._getLoginToken(connection.id); const expires = getRememberDate(); @@ -129,7 +129,16 @@ function rememberAuthorization(connection: IMethodConnection, user: IUser): void return; } - Users.setTwoFactorAuthorizationHashAndUntilForUserIdAndToken(user._id, currentToken, getFingerprintFromConnection(connection), expires); + if (!currentToken) { + return; + } + + await Users.setTwoFactorAuthorizationHashAndUntilForUserIdAndToken( + user._id, + currentToken, + getFingerprintFromConnection(connection), + expires, + ); } interface ICheckCodeForUser { @@ -158,7 +167,7 @@ const getSecondFactorMethod = (user: IUser, method: string | undefined, options: } }; -export function checkCodeForUser({ user, code, method, options = {}, connection }: ICheckCodeForUser): boolean { +export async function checkCodeForUser({ user, code, method, options = {}, connection }: ICheckCodeForUser): Promise { if (process.env.TEST_MODE && !options.requireSecondFactor) { return true; } @@ -167,8 +176,16 @@ export function checkCodeForUser({ user, code, method, options = {}, connection return true; } + let existingUser: IUser | null; + if (typeof user === 'string') { - user = getUserForCheck(user); + existingUser = await getUserForCheck(user); + } else { + existingUser = user; + } + + if (!existingUser) { + throw new Meteor.Error('totp-user-not-found', 'TOTP User not found'); } if (!code && !method && connection?.httpHeaders?.['x-2fa-code'] && connection.httpHeaders['x-2fa-method']) { @@ -176,20 +193,20 @@ export function checkCodeForUser({ user, code, method, options = {}, connection method = connection.httpHeaders['x-2fa-method']; } - if (connection && isAuthorizedForToken(connection, user, options)) { + if (connection && isAuthorizedForToken(connection, existingUser, options)) { return true; } // select a second factor method or return if none is found/available - const selectedMethod = getSecondFactorMethod(user, method, options); + const selectedMethod = getSecondFactorMethod(existingUser, method, options); if (!selectedMethod) { return true; } - const data = selectedMethod.processInvalidCode(user); + const data = await selectedMethod.processInvalidCode(existingUser); if (!code) { - const availableMethods = getAvailableMethodNames(user); + const availableMethods = getAvailableMethodNames(existingUser); throw new Meteor.Error('totp-required', 'TOTP Required', { method: selectedMethod.name, @@ -198,7 +215,7 @@ export function checkCodeForUser({ user, code, method, options = {}, connection }); } - const valid = selectedMethod.verify(user, code, options.requireSecondFactor); + const valid = await selectedMethod.verify(existingUser, code, options.requireSecondFactor); if (!valid) { throw new Meteor.Error('totp-invalid', 'TOTP Invalid', { method: selectedMethod.name, @@ -207,7 +224,7 @@ export function checkCodeForUser({ user, code, method, options = {}, connection } if (options.disableRememberMe !== true && connection) { - rememberAuthorization(connection, user); + await rememberAuthorization(connection, existingUser); } return true; diff --git a/apps/meteor/app/2fa/server/lib/totp.ts b/apps/meteor/app/2fa/server/lib/totp.ts index 365257d03e6..d1855f21cb8 100644 --- a/apps/meteor/app/2fa/server/lib/totp.ts +++ b/apps/meteor/app/2fa/server/lib/totp.ts @@ -1,8 +1,8 @@ import { SHA256 } from '@rocket.chat/sha256'; import { Random } from '@rocket.chat/random'; import speakeasy from 'speakeasy'; +import { Users } from '@rocket.chat/models'; -import { Users } from '../../../models/server'; import { settings } from '../../../settings/server'; export const TOTP = { @@ -17,17 +17,27 @@ export const TOTP = { }); }, - verify({ secret, token, backupTokens, userId }: { secret: string; token: string; backupTokens?: string[]; userId?: string }): boolean { + async verify({ + secret, + token, + backupTokens, + userId, + }: { + secret: string; + token: string; + backupTokens?: string[]; + userId?: string; + }): Promise { // validates a backup code if (token.length === 8 && backupTokens) { const hashedCode = SHA256(token); const usedCode = backupTokens.indexOf(hashedCode); - if (usedCode !== -1) { + if (usedCode !== -1 && userId) { backupTokens.splice(usedCode, 1); // mark the code as used (remove it from the list) - Users.update2FABackupCodesByUserId(userId, backupTokens); + await Users.update2FABackupCodesByUserId(userId, backupTokens); return true; } diff --git a/apps/meteor/app/2fa/server/loginHandler.ts b/apps/meteor/app/2fa/server/loginHandler.ts index ebce387299c..1366bbdf4dd 100644 --- a/apps/meteor/app/2fa/server/loginHandler.ts +++ b/apps/meteor/app/2fa/server/loginHandler.ts @@ -25,7 +25,7 @@ Accounts.registerLoginHandler('totp', function (options) { callbacks.add( 'onValidateLogin', - (login) => { + async (login) => { if (login.methodName === 'verifyEmail') { throw new Meteor.Error('verify-email', 'E-mail verified'); } @@ -45,7 +45,7 @@ callbacks.add( const [loginArgs] = login.methodArguments; const { totp } = loginArgs; - checkCodeForUser({ + await checkCodeForUser({ user: login.user, code: totp?.code, options: { disablePasswordFallback: true }, diff --git a/apps/meteor/app/2fa/server/methods/disable.ts b/apps/meteor/app/2fa/server/methods/disable.ts index 94e9e26b6e8..ad1adf21b50 100644 --- a/apps/meteor/app/2fa/server/methods/disable.ts +++ b/apps/meteor/app/2fa/server/methods/disable.ts @@ -1,13 +1,13 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; +import { Users } from '@rocket.chat/models'; -import { Users } from '../../../models/server'; import { TOTP } from '../lib/totp'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { - '2fa:disable': (code: string) => boolean; + '2fa:disable': (code: string) => Promise; } } @@ -26,7 +26,7 @@ Meteor.methods({ }); } - const verified = TOTP.verify({ + const verified = await TOTP.verify({ secret: user.services.totp.secret, token: code, userId, @@ -37,6 +37,6 @@ Meteor.methods({ return false; } - return Users.disable2FAByUserId(userId); + return (await Users.disable2FAByUserId(userId)).modifiedCount > 0; }, }); diff --git a/apps/meteor/app/2fa/server/methods/enable.ts b/apps/meteor/app/2fa/server/methods/enable.ts index 41d46f62733..8d68fb89dc1 100644 --- a/apps/meteor/app/2fa/server/methods/enable.ts +++ b/apps/meteor/app/2fa/server/methods/enable.ts @@ -1,13 +1,13 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; +import { Users } from '@rocket.chat/models'; -import { Users } from '../../../models/server'; import { TOTP } from '../lib/totp'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { - '2fa:enable': () => { secret: string; url: string }; + '2fa:enable': () => Promise<{ secret: string; url: string }>; } } @@ -36,7 +36,7 @@ Meteor.methods({ const secret = TOTP.generateSecret(); - Users.disable2FAAndSetTempSecretByUserId(userId, secret.base32); + await Users.disable2FAAndSetTempSecretByUserId(userId, secret.base32); return { secret: secret.base32, diff --git a/apps/meteor/app/2fa/server/methods/regenerateCodes.ts b/apps/meteor/app/2fa/server/methods/regenerateCodes.ts index 2b4af8c6e63..6bfe95b6107 100644 --- a/apps/meteor/app/2fa/server/methods/regenerateCodes.ts +++ b/apps/meteor/app/2fa/server/methods/regenerateCodes.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Users } from '@rocket.chat/models'; -import { Users } from '../../../models/server'; import { TOTP } from '../lib/totp'; declare module '@rocket.chat/ui-contexts' { @@ -29,7 +29,7 @@ Meteor.methods({ throw new Meteor.Error('invalid-totp'); } - const verified = TOTP.verify({ + const verified = await TOTP.verify({ secret: user.services.totp.secret, token: userToken, userId, @@ -39,7 +39,7 @@ Meteor.methods({ if (verified) { const { codes, hashedCodes } = TOTP.generateCodes(); - Users.update2FABackupCodesByUserId(Meteor.userId(), hashedCodes); + await Users.update2FABackupCodesByUserId(userId, hashedCodes); return { codes }; } }, diff --git a/apps/meteor/app/2fa/server/methods/validateTempToken.ts b/apps/meteor/app/2fa/server/methods/validateTempToken.ts index 2bcbe2980a8..71d90abe9dc 100644 --- a/apps/meteor/app/2fa/server/methods/validateTempToken.ts +++ b/apps/meteor/app/2fa/server/methods/validateTempToken.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import { Users } from '@rocket.chat/models'; -import { Users } from '../../../models/server'; import { TOTP } from '../lib/totp'; declare module '@rocket.chat/ui-contexts' { @@ -29,7 +29,7 @@ Meteor.methods({ throw new Meteor.Error('invalid-totp'); } - const verified = TOTP.verify({ + const verified = await TOTP.verify({ secret: user.services.totp.tempSecret, token: userToken, }); @@ -37,7 +37,7 @@ Meteor.methods({ if (verified) { const { codes, hashedCodes } = TOTP.generateCodes(); - Users.enable2FAAndSetSecretAndCodesByUserId(Meteor.userId(), user.services.totp.tempSecret, hashedCodes); + await Users.enable2FAAndSetSecretAndCodesByUserId(userId, user.services.totp.tempSecret, hashedCodes); return { codes }; } }, diff --git a/apps/meteor/app/2fa/server/twoFactorRequired.ts b/apps/meteor/app/2fa/server/twoFactorRequired.ts index 61a483da113..a3f77add66a 100644 --- a/apps/meteor/app/2fa/server/twoFactorRequired.ts +++ b/apps/meteor/app/2fa/server/twoFactorRequired.ts @@ -6,8 +6,8 @@ import { checkCodeForUser } from './code/index'; export function twoFactorRequired any>( fn: TFunction, options?: ITwoFactorOptions, -): (this: Meteor.MethodThisType, ...args: Parameters) => ReturnType { - return function (this: Meteor.MethodThisType, ...args: Parameters): ReturnType { +): (this: Meteor.MethodThisType, ...args: Parameters) => Promise> { + return async function (this: Meteor.MethodThisType, ...args: Parameters): Promise> { if (!this.userId) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'twoFactorRequired' }); } @@ -16,7 +16,7 @@ export function twoFactorRequired this.namedRoutes(route, endpoints, apiVersion)).map(addRateLimitRuleToEveryRoute); } - processTwoFactor({ userId, request, invocation, options, connection }) { + async processTwoFactor({ userId, request, invocation, options, connection }) { if (!options.twoFactorRequired) { return; } const code = request.headers['x-2fa-code']; const method = request.headers['x-2fa-method']; - checkCodeForUser({ user: userId, code, method, options: options.twoFactorOptions, connection }); + await checkCodeForUser({ user: userId, code, method, options: options.twoFactorOptions, connection }); invocation.twoFactorChecked = true; } @@ -444,7 +444,7 @@ export class APIClass extends Restivus { }; Accounts._setAccountData(connection.id, 'loginToken', this.token); - api.processTwoFactor({ + await api.processTwoFactor({ userId: this.userId, request: this.request, invocation, diff --git a/apps/meteor/app/api/server/v1/users.ts b/apps/meteor/app/api/server/v1/users.ts index c24436afec7..971906ab29c 100644 --- a/apps/meteor/app/api/server/v1/users.ts +++ b/apps/meteor/app/api/server/v1/users.ts @@ -754,7 +754,7 @@ API.v1.addRoute( ); API.v1.addRoute('users.2fa.sendEmailCode', { - post() { + async post() { const { emailOrUsername } = this.bodyParams; if (!emailOrUsername) { @@ -764,12 +764,13 @@ API.v1.addRoute('users.2fa.sendEmailCode', { const method = emailOrUsername.includes('@') ? 'findOneByEmailAddress' : 'findOneByUsername'; const userId = this.userId || Users[method](emailOrUsername, { fields: { _id: 1 } })?._id; - if (!userId) { + const user = await getUserForCheck(userId); + if (!userId || !user) { // this.logger.error('[2fa] User was not found when requesting 2fa email code'); return API.v1.success(); } - emailCheck.sendEmailCode(getUserForCheck(userId)); + await emailCheck.sendEmailCode(user); return API.v1.success(); }, @@ -1034,7 +1035,7 @@ API.v1.addRoute( } // this method logs the user out automatically, if successful returns 1, otherwise 0 - if (!Users.unsetLoginTokens(userId)) { + if (!(await UsersRaw.unsetLoginTokens(userId))) { throw new Meteor.Error('error-invalid-user-id', 'Invalid user id'); } diff --git a/apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts b/apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts index 2f25b92d40e..4c5432e87f4 100644 --- a/apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts +++ b/apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts @@ -7,7 +7,7 @@ import { resetUserE2EEncriptionKey } from '../../../../server/lib/resetUserE2EKe declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { - 'e2e.resetOwnE2EKey'(): boolean; + 'e2e.resetOwnE2EKey'(): Promise; } } diff --git a/apps/meteor/app/lib/server/functions/setUserActiveStatus.ts b/apps/meteor/app/lib/server/functions/setUserActiveStatus.ts index 5efda616973..b3f9f88f37b 100644 --- a/apps/meteor/app/lib/server/functions/setUserActiveStatus.ts +++ b/apps/meteor/app/lib/server/functions/setUserActiveStatus.ts @@ -3,7 +3,7 @@ import { check } from 'meteor/check'; import { Accounts } from 'meteor/accounts-base'; import type { IUser, IUserEmail } from '@rocket.chat/core-typings'; import { isUserFederated, isDirectMessageRoom } from '@rocket.chat/core-typings'; -import { Rooms as RoomsRaw } from '@rocket.chat/models'; +import { Rooms as RoomsRaw, Users as UsersRaw } from '@rocket.chat/models'; import * as Mailer from '../../../mailer/server/api'; import { Users, Subscriptions } from '../../../models/server'; @@ -104,7 +104,7 @@ export async function setUserActiveStatus(userId: string, active: boolean, confi } if (active === false) { - Users.unsetLoginTokens(userId); + await UsersRaw.unsetLoginTokens(userId); await RoomsRaw.setDmReadOnlyByUserId(userId, undefined, true, false); } else { Users.unsetReason(userId); diff --git a/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts b/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts index 07c86f22d9a..8445c08219e 100644 --- a/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts +++ b/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts @@ -8,7 +8,7 @@ import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { - insertOrUpdateUser(userData: Record): string | boolean; + insertOrUpdateUser(userData: Record): Promise; } } diff --git a/apps/meteor/app/lib/server/methods/saveSetting.ts b/apps/meteor/app/lib/server/methods/saveSetting.ts index 1ca5acabcf8..e920cda1a7e 100644 --- a/apps/meteor/app/lib/server/methods/saveSetting.ts +++ b/apps/meteor/app/lib/server/methods/saveSetting.ts @@ -11,7 +11,7 @@ import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { - saveSetting(_id: string, value: SettingValue, editor: string): boolean; + saveSetting(_id: string, value: SettingValue, editor: string): Promise; } } diff --git a/apps/meteor/app/models/server/models/Users.js b/apps/meteor/app/models/server/models/Users.js index bd15457efa5..fa80dbc16e3 100644 --- a/apps/meteor/app/models/server/models/Users.js +++ b/apps/meteor/app/models/server/models/Users.js @@ -378,55 +378,6 @@ export class Users extends Base { }; } - disable2FAAndSetTempSecretByUserId(userId, tempToken) { - return this.update( - { - _id: userId, - }, - { - $set: { - 'services.totp': { - enabled: false, - tempSecret: tempToken, - }, - }, - }, - ); - } - - enable2FAAndSetSecretAndCodesByUserId(userId, secret, backupCodes) { - return this.update( - { - _id: userId, - }, - { - $set: { - 'services.totp.enabled': true, - 'services.totp.secret': secret, - 'services.totp.hashedBackup': backupCodes, - }, - $unset: { - 'services.totp.tempSecret': 1, - }, - }, - ); - } - - disable2FAByUserId(userId) { - return this.update( - { - _id: userId, - }, - { - $set: { - 'services.totp': { - enabled: false, - }, - }, - }, - ); - } - addRoomByUserId(_id, rid) { return this.update( { @@ -499,19 +450,6 @@ export class Users extends Base { ); } - update2FABackupCodesByUserId(userId, backupCodes) { - return this.update( - { - _id: userId, - }, - { - $set: { - 'services.totp.hashedBackup': backupCodes, - }, - }, - ); - } - enableEmail2FAByUserId(userId) { return this.update( { @@ -568,51 +506,6 @@ export class Users extends Base { ); } - removeExpiredEmailCodesOfUserId(userId) { - this.update( - { _id: userId }, - { - $pull: { - 'services.emailCode': { - expire: { $lt: new Date() }, - }, - }, - }, - ); - } - - removeEmailCodeByUserIdAndCode(userId, code) { - this.update( - { _id: userId }, - { - $pull: { - 'services.emailCode': { - code, - }, - }, - }, - ); - } - - addEmailCodeByUserId(userId, code, expire) { - this.update( - { _id: userId }, - { - $push: { - 'services.emailCode': { - $each: [ - { - code, - expire, - }, - ], - $slice: -5, - }, - }, - }, - ); - } - /** * @param {IRole['_id'][]} roles the list of role ids * @param {null} scope the value for the role scope (room id) - not used in the users collection @@ -1361,21 +1254,6 @@ export class Users extends Base { return this.update(_id, update); } - setTwoFactorAuthorizationHashAndUntilForUserIdAndToken(_id, token, hash, until) { - return this.update( - { - _id, - 'services.resume.loginTokens.hashedToken': token, - }, - { - $set: { - 'services.resume.loginTokens.$.twoFactorAuthorizedHash': hash, - 'services.resume.loginTokens.$.twoFactorAuthorizedUntil': until, - }, - }, - ); - } - setUtcOffset(_id, utcOffset) { const query = { _id, diff --git a/apps/meteor/ee/server/lib/ldap/Manager.ts b/apps/meteor/ee/server/lib/ldap/Manager.ts index 0d10e436801..4d49d9e8f9b 100644 --- a/apps/meteor/ee/server/lib/ldap/Manager.ts +++ b/apps/meteor/ee/server/lib/ldap/Manager.ts @@ -603,7 +603,7 @@ export class LDAPEEManager extends LDAPManager { } if (this.isUserDeactivated(ldapUser)) { - UsersRaw.unsetLoginTokens(user._id); + await UsersRaw.unsetLoginTokens(user._id); } } } diff --git a/apps/meteor/imports/personal-access-tokens/server/api/methods/generateToken.ts b/apps/meteor/imports/personal-access-tokens/server/api/methods/generateToken.ts index 393c5681acc..ba772ff1ab7 100644 --- a/apps/meteor/imports/personal-access-tokens/server/api/methods/generateToken.ts +++ b/apps/meteor/imports/personal-access-tokens/server/api/methods/generateToken.ts @@ -10,7 +10,7 @@ import { twoFactorRequired } from '../../../../../app/2fa/server/twoFactorRequir declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { - 'personalAccessTokens:generateToken'(params: { tokenName: string; bypassTwoFactor: boolean }): string; + 'personalAccessTokens:generateToken'(params: { tokenName: string; bypassTwoFactor: boolean }): Promise; } } diff --git a/apps/meteor/imports/personal-access-tokens/server/api/methods/regenerateToken.ts b/apps/meteor/imports/personal-access-tokens/server/api/methods/regenerateToken.ts index fd704bac9d8..84964ec19d5 100644 --- a/apps/meteor/imports/personal-access-tokens/server/api/methods/regenerateToken.ts +++ b/apps/meteor/imports/personal-access-tokens/server/api/methods/regenerateToken.ts @@ -8,7 +8,7 @@ import { twoFactorRequired } from '../../../../../app/2fa/server/twoFactorRequir declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { - 'personalAccessTokens:regenerateToken'(params: { tokenName: string }): string; + 'personalAccessTokens:regenerateToken'(params: { tokenName: string }): Promise; } } diff --git a/apps/meteor/imports/personal-access-tokens/server/api/methods/removeToken.ts b/apps/meteor/imports/personal-access-tokens/server/api/methods/removeToken.ts index f4b7b1f3ec4..22fa837ee7e 100644 --- a/apps/meteor/imports/personal-access-tokens/server/api/methods/removeToken.ts +++ b/apps/meteor/imports/personal-access-tokens/server/api/methods/removeToken.ts @@ -8,7 +8,7 @@ import { twoFactorRequired } from '../../../../../app/2fa/server/twoFactorRequir declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { - 'personalAccessTokens:removeToken'(params: { tokenName: string }): void; + 'personalAccessTokens:removeToken'(params: { tokenName: string }): Promise; } } diff --git a/apps/meteor/server/lib/resetUserE2EKey.ts b/apps/meteor/server/lib/resetUserE2EKey.ts index 440cb326f9a..48db5c7e2cd 100644 --- a/apps/meteor/server/lib/resetUserE2EKey.ts +++ b/apps/meteor/server/lib/resetUserE2EKey.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import type { IUser } from '@rocket.chat/core-typings'; -import { Subscriptions } from '@rocket.chat/models'; +import { Subscriptions, Users as UsersRaw } from '@rocket.chat/models'; import { Users } from '../../app/models/server'; import { settings } from '../../app/settings/server'; @@ -72,7 +72,7 @@ export async function resetUserE2EEncriptionKey(uid: string, notifyUser: boolean await Subscriptions.resetUserE2EKey(uid); // Force the user to logout, so that the keys can be generated again - Users.unsetLoginTokens(uid); + await UsersRaw.unsetLoginTokens(uid); return true; }