diff --git a/packages/rocketchat-2fa/client/TOTPPassword.js b/packages/rocketchat-2fa/client/TOTPPassword.js index 7bb15b1fa738..50bb8e31ccb3 100644 --- a/packages/rocketchat-2fa/client/TOTPPassword.js +++ b/packages/rocketchat-2fa/client/TOTPPassword.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; -import { modal } from 'meteor/rocketchat:ui'; +import { modal } from 'meteor/rocketchat:ui-utils'; import { t } from 'meteor/rocketchat:utils'; import toastr from 'toastr'; diff --git a/packages/rocketchat-2fa/client/accountSecurity.js b/packages/rocketchat-2fa/client/accountSecurity.js index b99fb2fc3e49..3a530bd7b2ab 100644 --- a/packages/rocketchat-2fa/client/accountSecurity.js +++ b/packages/rocketchat-2fa/client/accountSecurity.js @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { Template } from 'meteor/templating'; -import { modal } from 'meteor/rocketchat:ui'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { modal } from 'meteor/rocketchat:ui-utils'; +import { settings } from 'meteor/rocketchat:settings'; import { t } from 'meteor/rocketchat:utils'; import toastr from 'toastr'; import qrcode from 'yaqrcode'; @@ -27,7 +27,7 @@ Template.accountSecurity.helpers({ return Template.instance().state.get() === 'registering'; }, isAllowed() { - return RocketChat.settings.get('Accounts_TwoFactorAuthentication_Enabled'); + return settings.get('Accounts_TwoFactorAuthentication_Enabled'); }, codesRemaining() { if (Template.instance().codesRemaining.get()) { diff --git a/packages/rocketchat-2fa/package.js b/packages/rocketchat-2fa/package.js index 71f3cf76599f..889045bb342c 100644 --- a/packages/rocketchat-2fa/package.js +++ b/packages/rocketchat-2fa/package.js @@ -11,11 +11,13 @@ Package.onUse(function(api) { 'accounts-base', 'ecmascript', 'templating', - 'rocketchat:lib', + 'rocketchat:settings', 'sha', 'random', - 'rocketchat:ui', + 'rocketchat:ui-utils', 'rocketchat:utils', + 'rocketchat:models', + 'rocketchat:callbacks', ]); api.mainModule('client/index.js', 'client'); diff --git a/packages/rocketchat-2fa/server/index.js b/packages/rocketchat-2fa/server/index.js index 2f088d337986..e5d5ab3fc445 100644 --- a/packages/rocketchat-2fa/server/index.js +++ b/packages/rocketchat-2fa/server/index.js @@ -1,6 +1,4 @@ import './startup/settings'; -import './lib/totp'; -import './models/users'; import './methods/checkCodesRemaining'; import './methods/disable'; import './methods/enable'; diff --git a/packages/rocketchat-2fa/server/lib/totp.js b/packages/rocketchat-2fa/server/lib/totp.js index 8d794d9ec359..2a1273c74ae8 100644 --- a/packages/rocketchat-2fa/server/lib/totp.js +++ b/packages/rocketchat-2fa/server/lib/totp.js @@ -1,9 +1,10 @@ import { SHA256 } from 'meteor/sha'; import { Random } from 'meteor/random'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Users } from 'meteor/rocketchat:models'; +import { settings } from 'meteor/rocketchat:settings'; import speakeasy from 'speakeasy'; -RocketChat.TOTP = { +export const TOTP = { generateSecret() { return speakeasy.generateSecret(); }, @@ -25,14 +26,14 @@ RocketChat.TOTP = { backupTokens.splice(usedCode, 1); // mark the code as used (remove it from the list) - RocketChat.models.Users.update2FABackupCodesByUserId(userId, backupTokens); + Users.update2FABackupCodesByUserId(userId, backupTokens); return true; } return false; } - const maxDelta = RocketChat.settings.get('Accounts_TwoFactorAuthentication_MaxDelta'); + const maxDelta = settings.get('Accounts_TwoFactorAuthentication_MaxDelta'); if (maxDelta) { const verifiedDelta = speakeasy.totp.verifyDelta({ secret, diff --git a/packages/rocketchat-2fa/server/loginHandler.js b/packages/rocketchat-2fa/server/loginHandler.js index 1060d218cb7a..31b9b37516c8 100644 --- a/packages/rocketchat-2fa/server/loginHandler.js +++ b/packages/rocketchat-2fa/server/loginHandler.js @@ -1,6 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { settings } from 'meteor/rocketchat:settings'; +import { callbacks } from 'meteor/rocketchat:callbacks'; +import { TOTP } from './lib/totp'; Accounts.registerLoginHandler('totp', function(options) { if (!options.totp || !options.totp.code) { @@ -10,8 +12,8 @@ Accounts.registerLoginHandler('totp', function(options) { return Accounts._runLoginHandlers(this, options.totp.login); }); -RocketChat.callbacks.add('onValidateLogin', (login) => { - if (!RocketChat.settings.get('Accounts_TwoFactorAuthentication_Enabled')) { +callbacks.add('onValidateLogin', (login) => { + if (!settings.get('Accounts_TwoFactorAuthentication_Enabled')) { return; } @@ -22,7 +24,7 @@ RocketChat.callbacks.add('onValidateLogin', (login) => { throw new Meteor.Error('totp-required', 'TOTP Required'); } - const verified = RocketChat.TOTP.verify({ + const verified = TOTP.verify({ secret: login.user.services.totp.secret, token: totp.code, userId: login.user._id, diff --git a/packages/rocketchat-2fa/server/methods/disable.js b/packages/rocketchat-2fa/server/methods/disable.js index ac68d9ddc5eb..ebec075d936e 100644 --- a/packages/rocketchat-2fa/server/methods/disable.js +++ b/packages/rocketchat-2fa/server/methods/disable.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Users } from 'meteor/rocketchat:models'; +import { TOTP } from '../lib/totp'; Meteor.methods({ '2fa:disable'(code) { @@ -9,7 +10,7 @@ Meteor.methods({ const user = Meteor.user(); - const verified = RocketChat.TOTP.verify({ + const verified = TOTP.verify({ secret: user.services.totp.secret, token: code, userId: Meteor.userId(), @@ -20,6 +21,6 @@ Meteor.methods({ return false; } - return RocketChat.models.Users.disable2FAByUserId(Meteor.userId()); + return Users.disable2FAByUserId(Meteor.userId()); }, }); diff --git a/packages/rocketchat-2fa/server/methods/enable.js b/packages/rocketchat-2fa/server/methods/enable.js index a2b984a4c1f1..4d73afc1ce84 100644 --- a/packages/rocketchat-2fa/server/methods/enable.js +++ b/packages/rocketchat-2fa/server/methods/enable.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Users } from 'meteor/rocketchat:models'; +import { TOTP } from '../lib/totp'; Meteor.methods({ '2fa:enable'() { @@ -9,13 +10,13 @@ Meteor.methods({ const user = Meteor.user(); - const secret = RocketChat.TOTP.generateSecret(); + const secret = TOTP.generateSecret(); - RocketChat.models.Users.disable2FAAndSetTempSecretByUserId(Meteor.userId(), secret.base32); + Users.disable2FAAndSetTempSecretByUserId(Meteor.userId(), secret.base32); return { secret: secret.base32, - url: RocketChat.TOTP.generateOtpauthURL(secret, user.username), + url: TOTP.generateOtpauthURL(secret, user.username), }; }, }); diff --git a/packages/rocketchat-2fa/server/methods/regenerateCodes.js b/packages/rocketchat-2fa/server/methods/regenerateCodes.js index 750879b8c60b..330b70c4bd57 100644 --- a/packages/rocketchat-2fa/server/methods/regenerateCodes.js +++ b/packages/rocketchat-2fa/server/methods/regenerateCodes.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Users } from 'meteor/rocketchat:models'; +import { TOTP } from '../lib/totp'; Meteor.methods({ '2fa:regenerateCodes'(userToken) { @@ -13,7 +14,7 @@ Meteor.methods({ throw new Meteor.Error('invalid-totp'); } - const verified = RocketChat.TOTP.verify({ + const verified = TOTP.verify({ secret: user.services.totp.secret, token: userToken, userId: Meteor.userId(), @@ -21,9 +22,9 @@ Meteor.methods({ }); if (verified) { - const { codes, hashedCodes } = RocketChat.TOTP.generateCodes(); + const { codes, hashedCodes } = TOTP.generateCodes(); - RocketChat.models.Users.update2FABackupCodesByUserId(Meteor.userId(), hashedCodes); + Users.update2FABackupCodesByUserId(Meteor.userId(), hashedCodes); return { codes }; } }, diff --git a/packages/rocketchat-2fa/server/methods/validateTempToken.js b/packages/rocketchat-2fa/server/methods/validateTempToken.js index a224f7b88b03..779b73bd73e7 100644 --- a/packages/rocketchat-2fa/server/methods/validateTempToken.js +++ b/packages/rocketchat-2fa/server/methods/validateTempToken.js @@ -1,5 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { RocketChat } from 'meteor/rocketchat:lib'; +import { Users } from 'meteor/rocketchat:models'; +import { TOTP } from '../lib/totp'; Meteor.methods({ '2fa:validateTempToken'(userToken) { @@ -13,15 +14,15 @@ Meteor.methods({ throw new Meteor.Error('invalid-totp'); } - const verified = RocketChat.TOTP.verify({ + const verified = TOTP.verify({ secret: user.services.totp.tempSecret, token: userToken, }); if (verified) { - const { codes, hashedCodes } = RocketChat.TOTP.generateCodes(); + const { codes, hashedCodes } = TOTP.generateCodes(); - RocketChat.models.Users.enable2FAAndSetSecretAndCodesByUserId(Meteor.userId(), user.services.totp.tempSecret, hashedCodes); + Users.enable2FAAndSetSecretAndCodesByUserId(Meteor.userId(), user.services.totp.tempSecret, hashedCodes); return { codes }; } }, diff --git a/packages/rocketchat-2fa/server/models/users.js b/packages/rocketchat-2fa/server/models/users.js deleted file mode 100644 index 80190c9cc3a1..000000000000 --- a/packages/rocketchat-2fa/server/models/users.js +++ /dev/null @@ -1,51 +0,0 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; - -RocketChat.models.Users.disable2FAAndSetTempSecretByUserId = function(userId, tempToken) { - return this.update({ - _id: userId, - }, { - $set: { - 'services.totp': { - enabled: false, - tempSecret: tempToken, - }, - }, - }); -}; - -RocketChat.models.Users.enable2FAAndSetSecretAndCodesByUserId = function(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, - }, - }); -}; - -RocketChat.models.Users.disable2FAByUserId = function(userId) { - return this.update({ - _id: userId, - }, { - $set: { - 'services.totp': { - enabled: false, - }, - }, - }); -}; - -RocketChat.models.Users.update2FABackupCodesByUserId = function(userId, backupCodes) { - return this.update({ - _id: userId, - }, { - $set: { - 'services.totp.hashedBackup': backupCodes, - }, - }); -}; diff --git a/packages/rocketchat-2fa/server/startup/settings.js b/packages/rocketchat-2fa/server/startup/settings.js index 63d5c6e37b15..18e3550e0b31 100644 --- a/packages/rocketchat-2fa/server/startup/settings.js +++ b/packages/rocketchat-2fa/server/startup/settings.js @@ -1,6 +1,6 @@ -import { RocketChat } from 'meteor/rocketchat:lib'; +import { settings } from 'meteor/rocketchat:settings'; -RocketChat.settings.addGroup('Accounts', function() { +settings.addGroup('Accounts', function() { this.section('Two Factor Authentication', function() { this.add('Accounts_TwoFactorAuthentication_Enabled', true, { type: 'boolean', diff --git a/packages/rocketchat-models/server/models/Users.js b/packages/rocketchat-models/server/models/Users.js index f90e64fa4a93..770c4a8ad9f5 100644 --- a/packages/rocketchat-models/server/models/Users.js +++ b/packages/rocketchat-models/server/models/Users.js @@ -52,6 +52,56 @@ 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, + }, + }, + }); + } + + update2FABackupCodesByUserId(userId, backupCodes) { + return this.update({ + _id: userId, + }, { + $set: { + 'services.totp.hashedBackup': backupCodes, + }, + }); + } + findByIdsWithPublicE2EKey(ids, options) { const query = { _id: {