diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index e3c242130..8a8985667 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -50,6 +50,7 @@ const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY!; const STRIPE_WEBHOOK_SECRET = process.env.STRIPE_WEBHOOK_SECRET!; const TELEMETRY_ENABLED = process.env.TELEMETRY_ENABLED! !== 'false' && true; const LICENSE_KEY = process.env.LICENSE_KEY!; +const SMTP_CONFIGURED = SMTP_HOST == '' || SMTP_HOST == undefined ? false : true export { PORT, @@ -101,5 +102,6 @@ export { STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, TELEMETRY_ENABLED, - LICENSE_KEY + LICENSE_KEY, + SMTP_CONFIGURED }; diff --git a/backend/src/controllers/v1/signupController.ts b/backend/src/controllers/v1/signupController.ts index 1c5c9e298..acedd40f1 100644 --- a/backend/src/controllers/v1/signupController.ts +++ b/backend/src/controllers/v1/signupController.ts @@ -1,7 +1,7 @@ import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; import { User } from '../../models'; -import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, INVITE_ONLY_SIGNUP } from '../../config'; +import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, INVITE_ONLY_SIGNUP, SMTP_CONFIGURED } from '../../config'; import { sendEmailVerification, checkEmailVerification, @@ -20,7 +20,6 @@ export const beginEmailSignup = async (req: Request, res: Response) => { let email: string; try { email = req.body.email; - if (INVITE_ONLY_SIGNUP) { // Only one user can create an account without being invited. The rest need to be invited in order to make an account const userCount = await User.countDocuments({}) @@ -75,10 +74,12 @@ export const verifyEmailSignup = async (req: Request, res: Response) => { } // verify email - await checkEmailVerification({ - email, - code - }); + if (SMTP_CONFIGURED) { + await checkEmailVerification({ + email, + code + }); + } if (!user) { user = await new User({ diff --git a/backend/src/controllers/v2/authController.ts b/backend/src/controllers/v2/authController.ts index 95a1613d2..58b7822b2 100644 --- a/backend/src/controllers/v2/authController.ts +++ b/backend/src/controllers/v2/authController.ts @@ -13,7 +13,7 @@ import { EELogService } from '../../ee/services'; import { NODE_ENV, JWT_MFA_LIFETIME, - JWT_MFA_SECRET + JWT_MFA_SECRET, } from '../../config'; import { BadRequestError, InternalServerError } from '../../utils/errors'; import { @@ -89,7 +89,7 @@ export const login1 = async (req: Request, res: Response) => { */ export const login2 = async (req: Request, res: Response) => { try { - + if (!req.headers['user-agent']) throw InternalServerError({ message: 'User-Agent header is required' }); const { email, clientProof } = req.body; @@ -129,12 +129,12 @@ export const login2 = async (req: Request, res: Response) => { expiresIn: JWT_MFA_LIFETIME, secret: JWT_MFA_SECRET }); - + const code = await TokenService.createToken({ type: TOKEN_EMAIL_MFA, email }); - + // send MFA code [code] to [email] await sendMail({ template: 'emailMfa.handlebars', @@ -144,13 +144,13 @@ export const login2 = async (req: Request, res: Response) => { code } }); - + return res.status(200).send({ mfaEnabled: true, token }); } - + await checkUserDevice({ user, ip: req.ip, @@ -183,7 +183,7 @@ export const login2 = async (req: Request, res: Response) => { iv?: string; tag?: string; } - + const response: ResponseData = { mfaEnabled: false, encryptionVersion: user.encryptionVersion, @@ -193,7 +193,7 @@ export const login2 = async (req: Request, res: Response) => { iv: user.iv, tag: user.tag } - + if ( user?.protectedKey && user?.protectedKeyIV && @@ -208,14 +208,14 @@ export const login2 = async (req: Request, res: Response) => { name: ACTION_LOGIN, userId: user._id }); - + loginAction && await EELogService.createLog({ userId: user._id, actions: [loginAction], channel: getChannelFromUserAgent(req.headers['user-agent']), ipAddress: req.ip }); - + return res.status(200).send(response); } @@ -246,7 +246,7 @@ export const sendMfaToken = async (req: Request, res: Response) => { type: TOKEN_EMAIL_MFA, email }); - + // send MFA code [code] to [email] await sendMail({ template: 'emailMfa.handlebars', @@ -261,9 +261,9 @@ export const sendMfaToken = async (req: Request, res: Response) => { Sentry.captureException(err); return res.status(400).send({ message: 'Failed to send MFA code' - }); + }); } - + return res.status(200).send({ message: 'Successfully sent new MFA code' }); @@ -276,76 +276,76 @@ export const sendMfaToken = async (req: Request, res: Response) => { * @param res */ export const verifyMfaToken = async (req: Request, res: Response) => { - const { email, mfaToken } = req.body; + const { email, mfaToken } = req.body; - await TokenService.validateToken({ - type: TOKEN_EMAIL_MFA, - email, - token: mfaToken - }); + await TokenService.validateToken({ + type: TOKEN_EMAIL_MFA, + email, + token: mfaToken + }); - const user = await User.findOne({ - email - }).select('+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag'); + const user = await User.findOne({ + email + }).select('+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag'); - if (!user) throw new Error('Failed to find user'); + if (!user) throw new Error('Failed to find user'); - await checkUserDevice({ - user, - ip: req.ip, - userAgent: req.headers['user-agent'] ?? '' - }); + await checkUserDevice({ + user, + ip: req.ip, + userAgent: req.headers['user-agent'] ?? '' + }); - // issue tokens - const tokens = await issueAuthTokens({ userId: user._id.toString() }); + // issue tokens + const tokens = await issueAuthTokens({ userId: user._id.toString() }); - // store (refresh) token in httpOnly cookie - res.cookie('jid', tokens.refreshToken, { - httpOnly: true, - path: '/', - sameSite: 'strict', - secure: NODE_ENV === 'production' ? true : false - }); - - interface VerifyMfaTokenRes { - encryptionVersion: number; - protectedKey?: string; - protectedKeyIV?: string; - protectedKeyTag?: string; - token: string; - publicKey: string; - encryptedPrivateKey: string; - iv: string; - tag: string; - } + // store (refresh) token in httpOnly cookie + res.cookie('jid', tokens.refreshToken, { + httpOnly: true, + path: '/', + sameSite: 'strict', + secure: NODE_ENV === 'production' ? true : false + }); - const resObj: VerifyMfaTokenRes = { - encryptionVersion: user.encryptionVersion, - token: tokens.token, - publicKey: user.publicKey as string, - encryptedPrivateKey: user.encryptedPrivateKey as string, - iv: user.iv as string, - tag: user.tag as string - } - - if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) { - resObj.protectedKey = user.protectedKey; - resObj.protectedKeyIV = user.protectedKeyIV; - resObj.protectedKeyTag = user.protectedKeyTag; - } + interface VerifyMfaTokenRes { + encryptionVersion: number; + protectedKey?: string; + protectedKeyIV?: string; + protectedKeyTag?: string; + token: string; + publicKey: string; + encryptedPrivateKey: string; + iv: string; + tag: string; + } - const loginAction = await EELogService.createAction({ - name: ACTION_LOGIN, - userId: user._id - }); - - loginAction && await EELogService.createLog({ - userId: user._id, - actions: [loginAction], - channel: getChannelFromUserAgent(req.headers['user-agent']), - ipAddress: req.ip - }); + const resObj: VerifyMfaTokenRes = { + encryptionVersion: user.encryptionVersion, + token: tokens.token, + publicKey: user.publicKey as string, + encryptedPrivateKey: user.encryptedPrivateKey as string, + iv: user.iv as string, + tag: user.tag as string + } - return res.status(200).send(resObj); + if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) { + resObj.protectedKey = user.protectedKey; + resObj.protectedKeyIV = user.protectedKeyIV; + resObj.protectedKeyTag = user.protectedKeyTag; + } + + const loginAction = await EELogService.createAction({ + name: ACTION_LOGIN, + userId: user._id + }); + + loginAction && await EELogService.createLog({ + userId: user._id, + actions: [loginAction], + channel: getChannelFromUserAgent(req.headers['user-agent']), + ipAddress: req.ip + }); + + return res.status(200).send(resObj); } diff --git a/backend/src/helpers/nodemailer.ts b/backend/src/helpers/nodemailer.ts index 958342aae..23872db3a 100644 --- a/backend/src/helpers/nodemailer.ts +++ b/backend/src/helpers/nodemailer.ts @@ -2,7 +2,7 @@ import fs from 'fs'; import path from 'path'; import handlebars from 'handlebars'; import nodemailer from 'nodemailer'; -import { SMTP_FROM_NAME, SMTP_FROM_ADDRESS } from '../config'; +import { SMTP_FROM_NAME, SMTP_FROM_ADDRESS, SMTP_CONFIGURED } from '../config'; import * as Sentry from '@sentry/node'; let smtpTransporter: nodemailer.Transporter; @@ -25,23 +25,25 @@ const sendMail = async ({ recipients: string[]; substitutions: any; }) => { - try { - const html = fs.readFileSync( - path.resolve(__dirname, '../templates/' + template), - 'utf8' - ); - const temp = handlebars.compile(html); - const htmlToSend = temp(substitutions); + if (SMTP_CONFIGURED) { + try { + const html = fs.readFileSync( + path.resolve(__dirname, '../templates/' + template), + 'utf8' + ); + const temp = handlebars.compile(html); + const htmlToSend = temp(substitutions); - await smtpTransporter.sendMail({ - from: `"${SMTP_FROM_NAME}" <${SMTP_FROM_ADDRESS}>`, - to: recipients.join(', '), - subject: subjectLine, - html: htmlToSend - }); - } catch (err) { - Sentry.setUser(null); - Sentry.captureException(err); + await smtpTransporter.sendMail({ + from: `"${SMTP_FROM_NAME}" <${SMTP_FROM_ADDRESS}>`, + to: recipients.join(', '), + subject: subjectLine, + html: htmlToSend + }); + } catch (err) { + Sentry.setUser(null); + Sentry.captureException(err); + } } }; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 021e3e0e5..5c92b590b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,5 +1,5 @@ { - "name": "frontend", + "name": "npm-proj-1677883018530-0.7603125731052582NtcmfK", "lockfileVersion": 2, "requires": true, "packages": { @@ -66,7 +66,7 @@ "react-table": "^7.8.0", "set-cookie-parser": "^2.5.1", "sharp": "^0.31.2", - "styled-components": "^5.3.5", + "styled-components": "^5.3.7", "tailwind-merge": "^1.8.1", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1", @@ -20245,10 +20245,9 @@ } }, "node_modules/styled-components": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.5.tgz", - "integrity": "sha512-ndETJ9RKaaL6q41B69WudeqLzOpY1A/ET/glXkNZ2T7dPjPqpPCXXQjDFYZWwNnE5co0wX+gTCqx9mfxTmSIPg==", - "hasInstallScript": true, + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.7.tgz", + "integrity": "sha512-JL1b4A79OGqav4TxkrNsuuQfy6ZnrpyQx6hBDQ3Hd3JyuR2IQuVNBpF+FCEWFNZpN5hj+fhkaEVWteVJ18f0tw==", "dependencies": { "@babel/helper-module-imports": "^7.0.0", "@babel/traverse": "^7.4.5", @@ -37001,9 +37000,9 @@ } }, "styled-components": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.5.tgz", - "integrity": "sha512-ndETJ9RKaaL6q41B69WudeqLzOpY1A/ET/glXkNZ2T7dPjPqpPCXXQjDFYZWwNnE5co0wX+gTCqx9mfxTmSIPg==", + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.7.tgz", + "integrity": "sha512-JL1b4A79OGqav4TxkrNsuuQfy6ZnrpyQx6hBDQ3Hd3JyuR2IQuVNBpF+FCEWFNZpN5hj+fhkaEVWteVJ18f0tw==", "requires": { "@babel/helper-module-imports": "^7.0.0", "@babel/traverse": "^7.4.5", diff --git a/frontend/package.json b/frontend/package.json index 3a61ed7b0..a1989701a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -73,7 +73,7 @@ "react-table": "^7.8.0", "set-cookie-parser": "^2.5.1", "sharp": "^0.31.2", - "styled-components": "^5.3.5", + "styled-components": "^5.3.7", "tailwind-merge": "^1.8.1", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1",