refactor: Remove Users from fibers use inside 2FA feature (#28641)

pull/28643/head^2
Marcos Spessatto Defendi 3 years ago committed by GitHub
parent c31a3862d7
commit 6c5cda74ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      apps/meteor/app/2fa/server/code/EmailCheck.ts
  2. 4
      apps/meteor/app/2fa/server/code/ICodeCheck.ts
  3. 4
      apps/meteor/app/2fa/server/code/PasswordCheckFallback.ts
  4. 4
      apps/meteor/app/2fa/server/code/TOTPCheck.ts
  5. 43
      apps/meteor/app/2fa/server/code/index.ts
  6. 18
      apps/meteor/app/2fa/server/lib/totp.ts
  7. 4
      apps/meteor/app/2fa/server/loginHandler.ts
  8. 8
      apps/meteor/app/2fa/server/methods/disable.ts
  9. 6
      apps/meteor/app/2fa/server/methods/enable.ts
  10. 6
      apps/meteor/app/2fa/server/methods/regenerateCodes.ts
  11. 6
      apps/meteor/app/2fa/server/methods/validateTempToken.ts
  12. 8
      apps/meteor/app/2fa/server/twoFactorRequired.ts
  13. 6
      apps/meteor/app/api/server/api.js
  14. 9
      apps/meteor/app/api/server/v1/users.ts
  15. 2
      apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts
  16. 4
      apps/meteor/app/lib/server/functions/setUserActiveStatus.ts
  17. 2
      apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts
  18. 2
      apps/meteor/app/lib/server/methods/saveSetting.ts
  19. 122
      apps/meteor/app/models/server/models/Users.js
  20. 2
      apps/meteor/ee/server/lib/ldap/Manager.ts
  21. 2
      apps/meteor/imports/personal-access-tokens/server/api/methods/generateToken.ts
  22. 2
      apps/meteor/imports/personal-access-tokens/server/api/methods/regenerateToken.ts
  23. 2
      apps/meteor/imports/personal-access-tokens/server/api/methods/removeToken.ts
  24. 4
      apps/meteor/server/lib/resetUserE2EKey.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<boolean> {
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<void> {
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<IProcessInvalidCodeResult> {
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,

@ -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<boolean>;
processInvalidCode(user: IUser): IProcessInvalidCodeResult;
processInvalidCode(user: IUser): Promise<IProcessInvalidCodeResult>;
}

@ -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<boolean> {
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<IProcessInvalidCodeResult> {
return {
codeGenerated: false,
};

@ -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<boolean> {
if (!this.isEnabled(user)) {
return false;
}
@ -32,7 +32,7 @@ export class TOTPCheck implements ICodeCheck {
});
}
public processInvalidCode(): IProcessInvalidCodeResult {
public async processInvalidCode(): Promise<IProcessInvalidCodeResult> {
// Nothing to do
return {
codeGenerated: false,

@ -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<IUser | null> {
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<void> {
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<boolean> {
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;

@ -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<boolean> {
// 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;
}

@ -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 },

@ -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<boolean>;
}
}
@ -26,7 +26,7 @@ Meteor.methods<ServerMethods>({
});
}
const verified = TOTP.verify({
const verified = await TOTP.verify({
secret: user.services.totp.secret,
token: code,
userId,
@ -37,6 +37,6 @@ Meteor.methods<ServerMethods>({
return false;
}
return Users.disable2FAByUserId(userId);
return (await Users.disable2FAByUserId(userId)).modifiedCount > 0;
},
});

@ -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<ServerMethods>({
const secret = TOTP.generateSecret();
Users.disable2FAAndSetTempSecretByUserId(userId, secret.base32);
await Users.disable2FAAndSetTempSecretByUserId(userId, secret.base32);
return {
secret: secret.base32,

@ -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<ServerMethods>({
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<ServerMethods>({
if (verified) {
const { codes, hashedCodes } = TOTP.generateCodes();
Users.update2FABackupCodesByUserId(Meteor.userId(), hashedCodes);
await Users.update2FABackupCodesByUserId(userId, hashedCodes);
return { codes };
}
},

@ -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<ServerMethods>({
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<ServerMethods>({
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 };
}
},

@ -6,8 +6,8 @@ import { checkCodeForUser } from './code/index';
export function twoFactorRequired<TFunction extends (this: Meteor.MethodThisType, ...args: any[]) => any>(
fn: TFunction,
options?: ITwoFactorOptions,
): (this: Meteor.MethodThisType, ...args: Parameters<TFunction>) => ReturnType<TFunction> {
return function (this: Meteor.MethodThisType, ...args: Parameters<TFunction>): ReturnType<TFunction> {
): (this: Meteor.MethodThisType, ...args: Parameters<TFunction>) => Promise<ReturnType<TFunction>> {
return async function (this: Meteor.MethodThisType, ...args: Parameters<TFunction>): Promise<ReturnType<TFunction>> {
if (!this.userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'twoFactorRequired' });
}
@ -16,7 +16,7 @@ export function twoFactorRequired<TFunction extends (this: Meteor.MethodThisType
const twoFactor = args.pop();
if (twoFactor) {
if (twoFactor.twoFactorCode && twoFactor.twoFactorMethod) {
checkCodeForUser({
await checkCodeForUser({
user: this.userId,
connection: this.connection || undefined,
code: twoFactor.twoFactorCode,
@ -31,7 +31,7 @@ export function twoFactorRequired<TFunction extends (this: Meteor.MethodThisType
}
if (!this.twoFactorChecked) {
checkCodeForUser({ user: this.userId, connection: this.connection || undefined, options });
await checkCodeForUser({ user: this.userId, connection: this.connection || undefined, options });
}
return fn.apply(this, args);

@ -281,14 +281,14 @@ export class APIClass extends Restivus {
routes.map((route) => 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,

@ -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');
}

@ -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<boolean>;
}
}

@ -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);

@ -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, unknown>): string | boolean;
insertOrUpdateUser(userData: Record<string, unknown>): Promise<string | boolean>;
}
}

@ -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<boolean>;
}
}

@ -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,

@ -603,7 +603,7 @@ export class LDAPEEManager extends LDAPManager {
}
if (this.isUserDeactivated(ldapUser)) {
UsersRaw.unsetLoginTokens(user._id);
await UsersRaw.unsetLoginTokens(user._id);
}
}
}

@ -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<string>;
}
}

@ -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<string>;
}
}

@ -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<void>;
}
}

@ -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;
}

Loading…
Cancel
Save