Skip to content

Commit

Permalink
Merge branch 'develop' into ROC-31
Browse files Browse the repository at this point in the history
  • Loading branch information
souzaramon committed May 13, 2022
2 parents 604dccf + 41d39ec commit 79443da
Show file tree
Hide file tree
Showing 1,405 changed files with 31,232 additions and 5,319 deletions.
4 changes: 4 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
/packages/* @RocketChat/chat-engine
/packages/core-typings/ @RocketChat/chat-engine
/packages/rest-typings/ @RocketChat/chat-engine
/packages/ui-contexts/ @RocketChat/frontend
/packages/eslint-config/ @RocketChat/chat-engine
/packages/livechat/ @RocketChat/frontend @RocketChat/chat-engine
/.vscode/ @RocketChat/chat-engine
/.github/ @RocketChat/chat-engine
/_templates/ @RocketChat/chat-engine
/apps/meteor/client/ @RocketChat/frontend
/apps/meteor/tests/ @RocketChat/chat-engine
2 changes: 1 addition & 1 deletion .github/no-js-action-config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"added": {
"ignore": ["packages/accounts-linkedin/**/*", "packages/linkedin-oauth/**/*", "tests/cypress/integration/08-resolutions.spec.js", "**/.eslintrc.js", "packages/eslint-config/**"]
"ignore": ["packages/accounts-linkedin/**/*", "packages/linkedin-oauth/**/*", "tests/cypress/integration/08-resolutions.spec.js", "**/.eslintrc.js", "packages/eslint-config/**", "**/babel.config.js"]
}
}
16 changes: 11 additions & 5 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ jobs:
run: yarn

- name: Build micro services
run: yarn build:services
run: yarn build

- name: E2E Test API
env:
Expand Down Expand Up @@ -617,14 +617,14 @@ jobs:
IMAGE_TAG: check
run: |
yarn
yarn build:services
yarn build
echo "Building Docker image for service: ${{ matrix.service }}:${IMAGE_TAG}"
docker build \
--build-arg SERVICE=${{ matrix.service }} \
-t rocketchat/${{ matrix.service }}-service:${IMAGE_TAG} \
-f ./apps/meteor/ee/server/services/Dockerfile \
-f ./ee/apps/ddp-streamer/Dockerfile \
.
release-versions:
Expand Down Expand Up @@ -872,14 +872,20 @@ jobs:
# first install repo dependencies
yarn
yarn build:services
yarn build
echo "Building Docker image for service: ${{ matrix.service }}:${IMAGE_TAG}"
if [[ "${{ matrix.service }}" == "ddp-streamer" ]]; then
DOCKERFILE_PATH="./ee/apps/ddp-streamer/Dockerfile"
else
DOCKERFILE_PATH="./apps/meteor/ee/server/services/Dockerfile"
fi
docker build \
--build-arg SERVICE=${{ matrix.service }} \
-t rocketchat/${{ matrix.service }}-service:${IMAGE_TAG} \
-f ./apps/meteor/ee/server/services/Dockerfile \
-f ${DOCKERFILE_PATH} \
.
docker push rocketchat/${{ matrix.service }}-service:${IMAGE_TAG}
Expand Down
5 changes: 3 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ Copyright (c) 2015-2022 Rocket.Chat Technologies Corp.

Portions of this software are licensed as follows:

* All content that resides under the "apps/meteor/ee/" directory of this repository, if
that directory exists, is licensed under the license defined in "apps/meteor/ee/LICENSE".
* All content that resides under the "apps/meteor/ee/" and "ee/" directories
of this repository, if that directory exists, is licensed under the license
defined in "apps/meteor/ee/LICENSE".
* All third-party components incorporated into the Rocket.Chat Software are
licensed under the original license provided by the owner of the applicable
component.
Expand Down
2 changes: 1 addition & 1 deletion _templates/package/new/package.json.ejs.t
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ to: packages/<%= name %>/package.json
"lint": "eslint --ext .js,.jsx,.ts,.tsx .",
"lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix",
"jest": "jest",
"build": "tsc -p tsconfig.json"
"build": "rm -rf dist && tsc -p tsconfig.json"
},
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
Expand Down
1 change: 1 addition & 0 deletions apps/meteor/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,4 @@ coverage
.nyc_output
/data
tests/e2e/test-failures/
out.txt
22 changes: 14 additions & 8 deletions apps/meteor/.scripts/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ function killAllProcesses(mainExitCode) {
}

function startProcess(opts) {
console.log('Starting process', opts.name, opts.command, opts.params, opts.options.cwd);
const proc = spawn(opts.command, opts.params, opts.options);
processes.push(proc);

Expand Down Expand Up @@ -125,20 +126,25 @@ function startRocketChat() {
}

async function startMicroservices() {
const waitStart = (resolve) => (message) => {
if (message.toString().match('NetworkBroker started successfully')) {
return resolve();
}
};
const startService = (name) => {
return new Promise((resolve) => {
const waitStart = (message) => {
if (message.toString().match('NetworkBroker started successfully')) {
return resolve();
}
};
const cwd =
name === 'ddp-streamer'
? path.resolve(srcDir, '..', '..', 'ee', 'apps', name, 'dist', 'ee', 'apps', name)
: path.resolve(srcDir, 'ee', 'server', 'services', 'dist', 'ee', 'server', 'services', name);

startProcess({
name: `${name} service`,
command: 'node',
params: ['service.js'],
onData: waitStart,
params: [name === 'ddp-streamer' ? 'src/service.js' : 'service.js'],
onData: waitStart(resolve),
options: {
cwd: path.resolve(srcDir, 'ee', 'server', 'services', 'dist', 'ee', 'server', 'services', name),
cwd,
env: {
...appOptions.env,
...process.env,
Expand Down
4 changes: 4 additions & 0 deletions apps/meteor/app/2fa/server/code/TOTPCheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export class TOTPCheck implements ICodeCheck {
return false;
}

if (!user.services?.totp?.secret) {
return false;
}

return TOTP.verify({
secret: user.services?.totp?.secret,
token: code,
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@ import { SHA256 } from 'meteor/sha';
import { Random } from 'meteor/random';
import speakeasy from 'speakeasy';

// @ts-expect-error
import { Users } from '../../../models';
import { settings } from '../../../settings/server';

export const TOTP = {
generateSecret() {
generateSecret(): speakeasy.GeneratedSecret {
return speakeasy.generateSecret();
},

generateOtpauthURL(secret, username) {
generateOtpauthURL(secret: speakeasy.GeneratedSecret, username: string): string {
return speakeasy.otpauthURL({
secret: secret.ascii,
label: `Rocket.Chat:${username}`,
});
},

verify({ secret, token, backupTokens, userId }) {
verify({ secret, token, backupTokens, userId }: { secret: string; token: string; backupTokens?: string[]; userId?: string }): boolean {
// validates a backup code
if (token.length === 8 && backupTokens) {
const hashedCode = SHA256(token);
Expand All @@ -34,7 +35,7 @@ export const TOTP = {
return false;
}

const maxDelta = settings.get('Accounts_TwoFactorAuthentication_MaxDelta');
const maxDelta = settings.get<number>('Accounts_TwoFactorAuthentication_MaxDelta');
if (maxDelta) {
const verifiedDelta = speakeasy.totp.verifyDelta({
secret,
Expand All @@ -53,7 +54,7 @@ export const TOTP = {
});
},

generateCodes() {
generateCodes(): { codes: string[]; hashedCodes: string[] } {
// generate 12 backup codes
const codes = [];
const hashedCodes = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,20 @@ import { check } from 'meteor/check';
import { callbacks } from '../../../lib/callbacks';
import { checkCodeForUser } from './code/index';

const isMeteorError = (error: any): error is Meteor.Error => {
return error?.meteorError !== undefined;
};

const isCredentialWithError = (credential: any): credential is { error: Error } => {
return credential?.error !== undefined;
};

Accounts.registerLoginHandler('totp', function (options) {
if (!options.totp || !options.totp.code) {
return;
}

// @ts-expect-error - not sure how to type this yet
return Accounts._runLoginHandlers(this, options.totp.login);
});

Expand All @@ -27,11 +36,15 @@ callbacks.add(
return login;
}

if (!login.user) {
return login;
}

const { totp } = loginArgs;

checkCodeForUser({
user: login.user,
code: totp && totp.code,
code: totp?.code,
options: { disablePasswordFallback: true },
});

Expand All @@ -41,24 +54,27 @@ callbacks.add(
'2fa',
);

const recreateError = (errorDoc) => {
let error;
const copyTo = <T extends Error>(from: T, to: T): T => {
Object.getOwnPropertyNames(to).forEach((key) => {
const idx: keyof T = key as keyof T;
to[idx] = from[idx];
});

if (errorDoc.meteorError) {
error = new Meteor.Error();
delete errorDoc.meteorError;
} else {
error = new Error();
return to;
};

const recreateError = (errorDoc: Error | Meteor.Error): Error | Meteor.Error => {
if (isMeteorError(errorDoc)) {
const error = new Meteor.Error('');
return copyTo(errorDoc, error);
}

Object.getOwnPropertyNames(errorDoc).forEach((key) => {
error[key] = errorDoc[key];
});
return error;
const error = new Error();
return copyTo(errorDoc, error);
};

OAuth._retrievePendingCredential = function (key, ...args) {
const credentialSecret = args.length > 0 && args[0] !== undefined ? args[0] : null;
OAuth._retrievePendingCredential = function (key, ...args): string | Error | void {
const credentialSecret = args.length > 0 && args[0] !== undefined ? args[0] : undefined;
check(key, String);

const pendingCredential = OAuth._pendingCredentials.findOne({
Expand All @@ -70,7 +86,7 @@ OAuth._retrievePendingCredential = function (key, ...args) {
return;
}

if (pendingCredential.credential.error) {
if (isCredentialWithError(pendingCredential.credential)) {
OAuth._pendingCredentials.remove({
_id: pendingCredential._id,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ Meteor.methods({

const user = Meteor.user();

if (!user) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: '2fa:checkCodesRemaining',
});
}

if (!user.services || !user.services.totp || !user.services.totp.enabled) {
throw new Meteor.Error('invalid-totp');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
import { Meteor } from 'meteor/meteor';

import { Users } from '../../../models';
import { Users } from '../../../models/server';
import { TOTP } from '../lib/totp';

Meteor.methods({
'2fa:disable'(code) {
if (!Meteor.userId()) {
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('not-authorized');
}

const user = Meteor.user();

if (!user) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: '2fa:disable',
});
}

const verified = TOTP.verify({
secret: user.services.totp.secret,
token: code,
userId: Meteor.userId(),
userId,
backupTokens: user.services.totp.hashedBackup,
});

if (!verified) {
return false;
}

return Users.disable2FAByUserId(Meteor.userId());
return Users.disable2FAByUserId(userId);
},
});
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { Meteor } from 'meteor/meteor';

import { Users } from '../../../models';
import { Users } from '../../../models/server';
import { TOTP } from '../lib/totp';

Meteor.methods({
'2fa:enable'() {
if (!Meteor.userId()) {
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('not-authorized');
}

const user = Meteor.user();

if (!user || !user.username) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: '2fa:enable',
});
}

const secret = TOTP.generateSecret();

Users.disable2FAAndSetTempSecretByUserId(Meteor.userId(), secret.base32);
Users.disable2FAAndSetTempSecretByUserId(userId, secret.base32);

return {
secret: secret.base32,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { Meteor } from 'meteor/meteor';

import { Users } from '../../../models';
import { Users } from '../../../models/server';
import { TOTP } from '../lib/totp';

Meteor.methods({
'2fa:regenerateCodes'(userToken) {
if (!Meteor.userId()) {
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('not-authorized');
}

const user = Meteor.user();
if (!user) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: '2fa:regenerateCodes',
});
}

if (!user.services || !user.services.totp || !user.services.totp.enabled) {
throw new Meteor.Error('invalid-totp');
Expand All @@ -18,7 +24,7 @@ Meteor.methods({
const verified = TOTP.verify({
secret: user.services.totp.secret,
token: userToken,
userId: Meteor.userId(),
userId,
backupTokens: user.services.totp.hashedBackup,
});

Expand Down
Loading

0 comments on commit 79443da

Please sign in to comment.