diff --git a/.env.example b/.env.example index c893713f4..79fca5686 100644 --- a/.env.example +++ b/.env.example @@ -16,9 +16,6 @@ JWT_AUTH_LIFETIME= JWT_REFRESH_LIFETIME= JWT_SIGNUP_LIFETIME= -# Optional lifetimes for OTP expressed in seconds -EMAIL_TOKEN_LIFETIME= - # MongoDB # Backend will connect to the MongoDB instance at connection string MONGO_URL which can either be a ref # to the MongoDB container instance or Mongo Cloud diff --git a/backend/environment.d.ts b/backend/environment.d.ts index 497902def..3793552b1 100644 --- a/backend/environment.d.ts +++ b/backend/environment.d.ts @@ -4,7 +4,6 @@ declare global { namespace NodeJS { interface ProcessEnv { PORT: string; - EMAIL_TOKEN_LIFETIME: string; ENCRYPTION_KEY: string; SALT_ROUNDS: string; JWT_AUTH_LIFETIME: string; diff --git a/backend/package-lock.json b/backend/package-lock.json index cef782117..5dbf691ca 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -32,6 +32,7 @@ "express-validator": "^6.14.2", "handlebars": "^4.7.7", "helmet": "^5.1.1", + "infisical-node": "^1.0.34", "js-yaml": "^4.1.0", "jsonwebtoken": "^9.0.0", "jsrp": "^0.2.4", @@ -5973,6 +5974,16 @@ "node": ">=0.8.19" } }, + "node_modules/infisical-node": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/infisical-node/-/infisical-node-1.0.34.tgz", + "integrity": "sha512-0joSgkNPZ15aZtm8Mrr/vSWizTYZlJivbawCecfllR4bzQ03TT3Ja4hivYyAmRYkrhUTHqb0gpQ3a8lSk7vyug==", + "dependencies": { + "axios": "^1.3.3", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -16758,6 +16769,16 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, + "infisical-node": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/infisical-node/-/infisical-node-1.0.34.tgz", + "integrity": "sha512-0joSgkNPZ15aZtm8Mrr/vSWizTYZlJivbawCecfllR4bzQ03TT3Ja4hivYyAmRYkrhUTHqb0gpQ3a8lSk7vyug==", + "requires": { + "axios": "^1.3.3", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + } + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", diff --git a/backend/package.json b/backend/package.json index 90db51ece..04a5c0943 100644 --- a/backend/package.json +++ b/backend/package.json @@ -23,6 +23,7 @@ "express-validator": "^6.14.2", "handlebars": "^4.7.7", "helmet": "^5.1.1", + "infisical-node": "^1.0.34", "js-yaml": "^4.1.0", "jsonwebtoken": "^9.0.0", "jsrp": "^0.2.4", diff --git a/backend/src/app.ts b/backend/src/app.ts deleted file mode 100644 index 550cf08f6..000000000 --- a/backend/src/app.ts +++ /dev/null @@ -1,144 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/no-var-requires -const { patchRouterParam } = require('./utils/patchAsyncRoutes'); - -import express from 'express'; -import helmet from 'helmet'; -import cors from 'cors'; -import cookieParser from 'cookie-parser'; -import dotenv from 'dotenv'; -import swaggerUi = require('swagger-ui-express'); -// eslint-disable-next-line @typescript-eslint/no-var-requires -const swaggerFile = require('../spec.json'); -// eslint-disable-next-line @typescript-eslint/no-var-requires -const requestIp = require('request-ip'); - -dotenv.config(); -import { PORT, NODE_ENV, SITE_URL } from './config'; -import { apiLimiter } from './helpers/rateLimiter'; - -import { - workspace as eeWorkspaceRouter, - secret as eeSecretRouter, - secretSnapshot as eeSecretSnapshotRouter, - action as eeActionRouter -} from './ee/routes/v1'; -import { - signup as v1SignupRouter, - auth as v1AuthRouter, - bot as v1BotRouter, - organization as v1OrganizationRouter, - workspace as v1WorkspaceRouter, - membershipOrg as v1MembershipOrgRouter, - membership as v1MembershipRouter, - key as v1KeyRouter, - inviteOrg as v1InviteOrgRouter, - user as v1UserRouter, - userAction as v1UserActionRouter, - secret as v1SecretRouter, - serviceToken as v1ServiceTokenRouter, - password as v1PasswordRouter, - stripe as v1StripeRouter, - integration as v1IntegrationRouter, - integrationAuth as v1IntegrationAuthRouter -} from './routes/v1'; -import { - signup as v2SignupRouter, - auth as v2AuthRouter, - users as v2UsersRouter, - organizations as v2OrganizationsRouter, - workspace as v2WorkspaceRouter, - secret as v2SecretRouter, // begin to phase out - secrets as v2SecretsRouter, - serviceTokenData as v2ServiceTokenDataRouter, - apiKeyData as v2APIKeyDataRouter, - environment as v2EnvironmentRouter, - tags as v2TagsRouter, -} from './routes/v2'; - -import { healthCheck } from './routes/status'; - -import { getLogger } from './utils/logger'; -import { RouteNotFoundError } from './utils/errors'; -import { requestErrorHandler } from './middleware/requestErrorHandler'; - -// patch async route params to handle Promise Rejections -patchRouterParam(); - -export const app = express(); - -app.enable('trust proxy'); -app.use(express.json()); -app.use(cookieParser()); -app.use( - cors({ - credentials: true, - origin: SITE_URL - }) -); - -app.use(requestIp.mw()) - -if (NODE_ENV === 'production') { - // enable app-wide rate-limiting + helmet security - // in production - app.disable('x-powered-by'); - app.use(apiLimiter); - app.use(helmet()); -} - -// (EE) routes -app.use('/api/v1/secret', eeSecretRouter); -app.use('/api/v1/secret-snapshot', eeSecretSnapshotRouter); -app.use('/api/v1/workspace', eeWorkspaceRouter); -app.use('/api/v1/action', eeActionRouter); - -// v1 routes -app.use('/api/v1/signup', v1SignupRouter); -app.use('/api/v1/auth', v1AuthRouter); -app.use('/api/v1/bot', v1BotRouter); -app.use('/api/v1/user', v1UserRouter); -app.use('/api/v1/user-action', v1UserActionRouter); -app.use('/api/v1/organization', v1OrganizationRouter); -app.use('/api/v1/workspace', v1WorkspaceRouter); -app.use('/api/v1/membership-org', v1MembershipOrgRouter); -app.use('/api/v1/membership', v1MembershipRouter); -app.use('/api/v1/key', v1KeyRouter); -app.use('/api/v1/invite-org', v1InviteOrgRouter); -app.use('/api/v1/secret', v1SecretRouter); -app.use('/api/v1/service-token', v1ServiceTokenRouter); // deprecated -app.use('/api/v1/password', v1PasswordRouter); -app.use('/api/v1/stripe', v1StripeRouter); -app.use('/api/v1/integration', v1IntegrationRouter); -app.use('/api/v1/integration-auth', v1IntegrationAuthRouter); - -// v2 routes -app.use('/api/v2/signup', v2SignupRouter); -app.use('/api/v2/auth', v2AuthRouter); -app.use('/api/v2/users', v2UsersRouter); -app.use('/api/v2/organizations', v2OrganizationsRouter); -app.use('/api/v2/workspace', v2EnvironmentRouter); -app.use('/api/v2/workspace', v2TagsRouter); -app.use('/api/v2/workspace', v2WorkspaceRouter); -app.use('/api/v2/secret', v2SecretRouter); // deprecated -app.use('/api/v2/secrets', v2SecretsRouter); -app.use('/api/v2/service-token', v2ServiceTokenDataRouter); // TODO: turn into plural route -app.use('/api/v2/api-key', v2APIKeyDataRouter); - -// api docs -app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerFile)) - -// Server status -app.use('/api', healthCheck) - -//* Handle unrouted requests and respond with proper error message as well as status code -app.use((req, res, next) => { - if (res.headersSent) return next(); - next(RouteNotFoundError({ message: `The requested source '(${req.method})${req.url}' was not found` })) -}) - -//* Error Handling Middleware (must be after all routing logic) -app.use(requestErrorHandler) - -export const server = app.listen(PORT, () => { - getLogger("backend-main").info(`Server started listening at port ${PORT}`) -}); diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index e3c242130..06ad3a631 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,105 +1,101 @@ -const PORT = process.env.PORT || 4000; -const EMAIL_TOKEN_LIFETIME = parseInt(process.env.EMAIL_TOKEN_LIFETIME! || '86400'); -const INVITE_ONLY_SIGNUP = process.env.INVITE_ONLY_SIGNUP == undefined ? false : process.env.INVITE_ONLY_SIGNUP -const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY!; -const SALT_ROUNDS = parseInt(process.env.SALT_ROUNDS!) || 10; -const JWT_AUTH_LIFETIME = process.env.JWT_AUTH_LIFETIME! || '10d'; -const JWT_AUTH_SECRET = process.env.JWT_AUTH_SECRET!; -const JWT_MFA_LIFETIME = process.env.JWT_MFA_LIFETIME! || '5m'; -const JWT_MFA_SECRET = process.env.JWT_MFA_SECRET!; -const JWT_REFRESH_LIFETIME = process.env.JWT_REFRESH_LIFETIME! || '90d'; -const JWT_REFRESH_SECRET = process.env.JWT_REFRESH_SECRET!; -const JWT_SERVICE_SECRET = process.env.JWT_SERVICE_SECRET!; -const JWT_SIGNUP_LIFETIME = process.env.JWT_SIGNUP_LIFETIME! || '15m'; -const JWT_SIGNUP_SECRET = process.env.JWT_SIGNUP_SECRET!; -const MONGO_URL = process.env.MONGO_URL!; -const NODE_ENV = process.env.NODE_ENV! || 'production'; -const VERBOSE_ERROR_OUTPUT = process.env.VERBOSE_ERROR_OUTPUT! === 'true' && true; -const LOKI_HOST = process.env.LOKI_HOST || undefined; -const CLIENT_ID_AZURE = process.env.CLIENT_ID_AZURE!; -const CLIENT_ID_HEROKU = process.env.CLIENT_ID_HEROKU!; -const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!; -const CLIENT_ID_NETLIFY = process.env.CLIENT_ID_NETLIFY!; -const CLIENT_ID_GITHUB = process.env.CLIENT_ID_GITHUB!; -const CLIENT_ID_GITLAB = process.env.CLIENT_ID_GITLAB!; -const CLIENT_SECRET_AZURE = process.env.CLIENT_SECRET_AZURE!; -const CLIENT_SECRET_HEROKU = process.env.CLIENT_SECRET_HEROKU!; -const CLIENT_SECRET_VERCEL = process.env.CLIENT_SECRET_VERCEL!; -const CLIENT_SECRET_NETLIFY = process.env.CLIENT_SECRET_NETLIFY!; -const CLIENT_SECRET_GITHUB = process.env.CLIENT_SECRET_GITHUB!; -const CLIENT_SECRET_GITLAB = process.env.CLIENT_SECRET_GITLAB; -const CLIENT_SLUG_VERCEL = process.env.CLIENT_SLUG_VERCEL!; -const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com'; -const POSTHOG_PROJECT_API_KEY = - process.env.POSTHOG_PROJECT_API_KEY! || - 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE'; -const SENTRY_DSN = process.env.SENTRY_DSN!; -const SITE_URL = process.env.SITE_URL!; -const SMTP_HOST = process.env.SMTP_HOST!; -const SMTP_SECURE = process.env.SMTP_SECURE! === 'true' || false; -const SMTP_PORT = parseInt(process.env.SMTP_PORT!) || 587; -const SMTP_USERNAME = process.env.SMTP_USERNAME!; -const SMTP_PASSWORD = process.env.SMTP_PASSWORD!; -const SMTP_FROM_ADDRESS = process.env.SMTP_FROM_ADDRESS!; -const SMTP_FROM_NAME = process.env.SMTP_FROM_NAME! || 'Infisical'; -const STRIPE_PRODUCT_STARTER = process.env.STRIPE_PRODUCT_STARTER!; -const STRIPE_PRODUCT_PRO = process.env.STRIPE_PRODUCT_PRO!; -const STRIPE_PRODUCT_TEAM = process.env.STRIPE_PRODUCT_TEAM!; -const STRIPE_PUBLISHABLE_KEY = process.env.STRIPE_PUBLISHABLE_KEY!; -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 PORT = process.env.PORT || 4000; +// const INVITE_ONLY_SIGNUP = process.env.INVITE_ONLY_SIGNUP == undefined ? false : process.env.INVITE_ONLY_SIGNUP +// const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY!; +// const SALT_ROUNDS = parseInt(process.env.SALT_ROUNDS!) || 10; +// const JWT_AUTH_LIFETIME = process.env.JWT_AUTH_LIFETIME! || '10d'; +// const JWT_AUTH_SECRET = process.env.JWT_AUTH_SECRET!; +// const JWT_MFA_LIFETIME = process.env.JWT_MFA_LIFETIME! || '5m'; +// const JWT_MFA_SECRET = process.env.JWT_MFA_SECRET!; +// const JWT_REFRESH_LIFETIME = process.env.JWT_REFRESH_LIFETIME! || '90d'; +// const JWT_REFRESH_SECRET = process.env.JWT_REFRESH_SECRET!; +// const JWT_SERVICE_SECRET = process.env.JWT_SERVICE_SECRET!; +// const JWT_SIGNUP_LIFETIME = process.env.JWT_SIGNUP_LIFETIME! || '15m'; +// const JWT_SIGNUP_SECRET = process.env.JWT_SIGNUP_SECRET!; +// const MONGO_URL = process.env.MONGO_URL!; +// const NODE_ENV = process.env.NODE_ENV! || 'production'; +// const VERBOSE_ERROR_OUTPUT = process.env.VERBOSE_ERROR_OUTPUT! === 'true' && true; +// const LOKI_HOST = process.env.LOKI_HOST || undefined; +// const CLIENT_ID_AZURE = process.env.CLIENT_ID_AZURE!; +// const CLIENT_ID_HEROKU = process.env.CLIENT_ID_HEROKU!; +// const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!; +// const CLIENT_ID_NETLIFY = process.env.CLIENT_ID_NETLIFY!; +// const CLIENT_ID_GITHUB = process.env.CLIENT_ID_GITHUB!; +// const CLIENT_ID_GITLAB = process.env.CLIENT_ID_GITLAB!; +// const CLIENT_SECRET_AZURE = process.env.CLIENT_SECRET_AZURE!; +// const CLIENT_SECRET_HEROKU = process.env.CLIENT_SECRET_HEROKU!; +// const CLIENT_SECRET_VERCEL = process.env.CLIENT_SECRET_VERCEL!; +// const CLIENT_SECRET_NETLIFY = process.env.CLIENT_SECRET_NETLIFY!; +// const CLIENT_SECRET_GITHUB = process.env.CLIENT_SECRET_GITHUB!; +// const CLIENT_SECRET_GITLAB = process.env.CLIENT_SECRET_GITLAB; +// const CLIENT_SLUG_VERCEL = process.env.CLIENT_SLUG_VERCEL!; +// const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com'; +// const POSTHOG_PROJECT_API_KEY = + // process.env.POSTHOG_PROJECT_API_KEY! || + // 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE'; +// const SENTRY_DSN = process.env.SENTRY_DSN!; +// const SITE_URL = process.env.SITE_URL!; +// const SMTP_HOST = process.env.SMTP_HOST!; +// const SMTP_SECURE = process.env.SMTP_SECURE! === 'true' || false; +// const SMTP_PORT = parseInt(process.env.SMTP_PORT!) || 587; +// const SMTP_USERNAME = process.env.SMTP_USERNAME!; +// const SMTP_PASSWORD = process.env.SMTP_PASSWORD!; +// const SMTP_FROM_ADDRESS = process.env.SMTP_FROM_ADDRESS!; +// const SMTP_FROM_NAME = process.env.SMTP_FROM_NAME! || 'Infisical'; +// const STRIPE_PRODUCT_STARTER = process.env.STRIPE_PRODUCT_STARTER!; +// const STRIPE_PRODUCT_PRO = process.env.STRIPE_PRODUCT_PRO!; +// const STRIPE_PRODUCT_TEAM = process.env.STRIPE_PRODUCT_TEAM!; +// const STRIPE_PUBLISHABLE_KEY = process.env.STRIPE_PUBLISHABLE_KEY!; +// 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; export { - PORT, - EMAIL_TOKEN_LIFETIME, - INVITE_ONLY_SIGNUP, - ENCRYPTION_KEY, - SALT_ROUNDS, - JWT_AUTH_LIFETIME, - JWT_AUTH_SECRET, - JWT_MFA_LIFETIME, - JWT_MFA_SECRET, - JWT_REFRESH_LIFETIME, - JWT_REFRESH_SECRET, - JWT_SERVICE_SECRET, - JWT_SIGNUP_LIFETIME, - JWT_SIGNUP_SECRET, - MONGO_URL, - NODE_ENV, - VERBOSE_ERROR_OUTPUT, - LOKI_HOST, - CLIENT_ID_AZURE, - CLIENT_ID_HEROKU, - CLIENT_ID_VERCEL, - CLIENT_ID_NETLIFY, - CLIENT_ID_GITHUB, - CLIENT_ID_GITLAB, - CLIENT_SECRET_AZURE, - CLIENT_SECRET_HEROKU, - CLIENT_SECRET_VERCEL, - CLIENT_SECRET_NETLIFY, - CLIENT_SECRET_GITHUB, - CLIENT_SECRET_GITLAB, - CLIENT_SLUG_VERCEL, - POSTHOG_HOST, - POSTHOG_PROJECT_API_KEY, - SENTRY_DSN, - SITE_URL, - SMTP_HOST, - SMTP_PORT, - SMTP_SECURE, - SMTP_USERNAME, - SMTP_PASSWORD, - SMTP_FROM_ADDRESS, - SMTP_FROM_NAME, - STRIPE_PRODUCT_STARTER, - STRIPE_PRODUCT_TEAM, - STRIPE_PRODUCT_PRO, - STRIPE_PUBLISHABLE_KEY, - STRIPE_SECRET_KEY, - STRIPE_WEBHOOK_SECRET, - TELEMETRY_ENABLED, - LICENSE_KEY + // PORT, + // INVITE_ONLY_SIGNUP, + // ENCRYPTION_KEY, + // SALT_ROUNDS, + // JWT_AUTH_LIFETIME, + // JWT_AUTH_SECRET, + // JWT_MFA_LIFETIME, + // JWT_MFA_SECRET, + // JWT_REFRESH_LIFETIME, + // JWT_REFRESH_SECRET, + // JWT_SERVICE_SECRET, + // JWT_SIGNUP_LIFETIME, + // JWT_SIGNUP_SECRET, + // MONGO_URL, + // NODE_ENV, + // VERBOSE_ERROR_OUTPUT, + // LOKI_HOST, + // CLIENT_ID_AZURE, + // CLIENT_ID_HEROKU, + // CLIENT_ID_VERCEL, + // CLIENT_ID_NETLIFY, + // CLIENT_ID_GITHUB, + // CLIENT_ID_GITLAB, + // CLIENT_SECRET_AZURE, + // CLIENT_SECRET_HEROKU, + // CLIENT_SECRET_VERCEL, + // CLIENT_SECRET_NETLIFY, + // CLIENT_SECRET_GITHUB, + // CLIENT_SECRET_GITLAB, + // CLIENT_SLUG_VERCEL, + // POSTHOG_HOST, + // POSTHOG_PROJECT_API_KEY, + // SENTRY_DSN, + // SITE_URL, + // SMTP_HOST, + // SMTP_PORT, + // SMTP_SECURE, + // SMTP_USERNAME, + // SMTP_PASSWORD, + // SMTP_FROM_ADDRESS, + // SMTP_FROM_NAME, + // STRIPE_PRODUCT_STARTER, + // STRIPE_PRODUCT_TEAM, + // STRIPE_PRODUCT_PRO, + // STRIPE_PUBLISHABLE_KEY, + // STRIPE_SECRET_KEY, + // STRIPE_WEBHOOK_SECRET, + // TELEMETRY_ENABLED, }; diff --git a/backend/src/controllers/v1/authController.ts b/backend/src/controllers/v1/authController.ts index e329b15c7..732715967 100644 --- a/backend/src/controllers/v1/authController.ts +++ b/backend/src/controllers/v1/authController.ts @@ -1,8 +1,9 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ +import * as Sentry from '@sentry/node'; +import infisical from 'infisical-node'; import { Request, Response } from 'express'; import jwt from 'jsonwebtoken'; -import * as Sentry from '@sentry/node'; import * as bigintConversion from 'bigint-conversion'; +// eslint-disable-next-line @typescript-eslint/no-var-requires const jsrp = require('jsrp'); import { User, LoginSRPDetail } from '../../models'; import { createToken, issueAuthTokens, clearTokens } from '../../helpers/auth'; @@ -11,12 +12,6 @@ import { ACTION_LOGIN, ACTION_LOGOUT } from '../../variables'; -import { - NODE_ENV, - JWT_AUTH_LIFETIME, - JWT_AUTH_SECRET, - JWT_REFRESH_SECRET -} from '../../config'; import { BadRequestError } from '../../utils/errors'; import { EELogService } from '../../ee/services'; import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this @@ -126,7 +121,7 @@ export const login2 = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: NODE_ENV === 'production' ? true : false + secure: infisical.get('NODE_ENV')! === 'production' ? true : false }); const loginAction = await EELogService.createAction({ @@ -182,7 +177,7 @@ export const logout = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: NODE_ENV === 'production' ? true : false + secure: infisical.get('NODE_ENV') === 'production' ? true : false }); const logoutAction = await EELogService.createAction({ @@ -237,7 +232,7 @@ export const getNewToken = async (req: Request, res: Response) => { } const decodedToken = ( - jwt.verify(refreshToken, JWT_REFRESH_SECRET) + jwt.verify(refreshToken, infisical.get('JWT_REFRESH_SECRET')!) ); const user = await User.findOne({ @@ -252,8 +247,8 @@ export const getNewToken = async (req: Request, res: Response) => { payload: { userId: decodedToken.userId }, - expiresIn: JWT_AUTH_LIFETIME, - secret: JWT_AUTH_SECRET + expiresIn: infisical.get('JWT_AUTH_LIFETIME')!, + secret: infisical.get('JWT_AUTH_SECRET')! }); return res.status(200).send({ diff --git a/backend/src/controllers/v1/integrationAuthController.ts b/backend/src/controllers/v1/integrationAuthController.ts index d26bab1cc..da8fc5570 100644 --- a/backend/src/controllers/v1/integrationAuthController.ts +++ b/backend/src/controllers/v1/integrationAuthController.ts @@ -5,7 +5,7 @@ import { IntegrationAuth, Bot } from '../../models'; -import { INTEGRATION_SET, INTEGRATION_OPTIONS } from '../../variables'; +import { INTEGRATION_SET, getIntegrationOptions as getIntegrationOptionsFunc } from '../../variables'; import { IntegrationService } from '../../services'; import { getApps, @@ -39,9 +39,11 @@ export const getIntegrationAuth = async (req: Request, res: Response) => { } export const getIntegrationOptions = async (req: Request, res: Response) => { - return res.status(200).send({ - integrationOptions: INTEGRATION_OPTIONS, - }); + const INTEGRATION_OPTIONS = getIntegrationOptionsFunc(); + + return res.status(200).send({ + integrationOptions: INTEGRATION_OPTIONS, + }); }; /** diff --git a/backend/src/controllers/v1/membershipController.ts b/backend/src/controllers/v1/membershipController.ts index 3627ce3fb..11fc465c2 100644 --- a/backend/src/controllers/v1/membershipController.ts +++ b/backend/src/controllers/v1/membershipController.ts @@ -1,12 +1,12 @@ -import { Request, Response } from 'express'; +import infisical from 'infisical-node'; import * as Sentry from '@sentry/node'; -import { Membership, MembershipOrg, User, Key, IMembership, Workspace } from '../../models'; +import { Request, Response } from 'express'; +import { Membership, MembershipOrg, User, Key } from '../../models'; import { findMembership, deleteMembership as deleteMember } from '../../helpers/membership'; import { sendMail } from '../../helpers/nodemailer'; -import { SITE_URL } from '../../config'; import { ADMIN, MEMBER, ACCEPTED } from '../../variables'; /** @@ -215,7 +215,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => { inviterFirstName: req.user.firstName, inviterEmail: req.user.email, workspaceName: req.membership.workspace.name, - callback_url: SITE_URL + '/login' + callback_url: infisical.get('SITE_URL')! + '/login' } }); } catch (err) { diff --git a/backend/src/controllers/v1/membershipOrgController.ts b/backend/src/controllers/v1/membershipOrgController.ts index aa3022eac..0efe00dac 100644 --- a/backend/src/controllers/v1/membershipOrgController.ts +++ b/backend/src/controllers/v1/membershipOrgController.ts @@ -1,6 +1,6 @@ +import infisical from 'infisical-node'; import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; -import { SITE_URL, JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET } from '../../config'; import { MembershipOrg, Organization, User } from '../../models'; import { deleteMembershipOrg as deleteMemberFromOrg } from '../../helpers/membershipOrg'; import { createToken } from '../../helpers/auth'; @@ -178,7 +178,7 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => { organizationName: organization.name, email: inviteeEmail, token, - callback_url: SITE_URL + '/signupinvite' + callback_url: infisical.get('SITE_URL') + '/signupinvite' } }); } @@ -250,8 +250,8 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => { payload: { userId: user._id.toString() }, - expiresIn: JWT_SIGNUP_LIFETIME, - secret: JWT_SIGNUP_SECRET + expiresIn: infisical.get('JWT_SIGNUP_LIFETIME')!, + secret: infisical.get('JWT_SIGNUP_SECRET')! }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/controllers/v1/organizationController.ts b/backend/src/controllers/v1/organizationController.ts index 66326e560..44403db86 100644 --- a/backend/src/controllers/v1/organizationController.ts +++ b/backend/src/controllers/v1/organizationController.ts @@ -1,21 +1,13 @@ -import { Request, Response } from 'express'; +import infisical from 'infisical-node'; import * as Sentry from '@sentry/node'; -import { - SITE_URL, - STRIPE_SECRET_KEY -} from '../../config'; +import { Request, Response } from 'express'; import Stripe from 'stripe'; - -const stripe = new Stripe(STRIPE_SECRET_KEY, { - apiVersion: '2022-08-01' -}); import { Membership, MembershipOrg, Organization, Workspace, - IncidentContactOrg, - IMembershipOrg + IncidentContactOrg } from '../../models'; import { createOrganization as create } from '../../helpers/organization'; import { addMembershipsOrg } from '../../helpers/membershipOrg'; @@ -325,6 +317,10 @@ export const createOrganizationPortalSession = async ( ) => { let session; try { + const stripe = new Stripe(infisical.get('STRIPE_SECRET_KEY')!, { + apiVersion: '2022-08-01' + }); + // check if there is a payment method on file const paymentMethods = await stripe.paymentMethods.list({ customer: req.membershipOrg.organization.customerId, @@ -337,13 +333,13 @@ export const createOrganizationPortalSession = async ( customer: req.membershipOrg.organization.customerId, mode: 'setup', payment_method_types: ['card'], - success_url: SITE_URL + '/dashboard', - cancel_url: SITE_URL + '/dashboard' + success_url: infisical.get('SITE_URL')! + '/dashboard', + cancel_url: infisical.get('SITE_URL')! + '/dashboard' }); } else { session = await stripe.billingPortal.sessions.create({ customer: req.membershipOrg.organization.customerId, - return_url: SITE_URL + '/dashboard' + return_url: infisical.get('SITE_URL') + '/dashboard' }); } @@ -369,6 +365,10 @@ export const getOrganizationSubscriptions = async ( ) => { let subscriptions; try { + const stripe = new Stripe(infisical.get('STRIPE_SECRET_KEY')!, { + apiVersion: '2022-08-01' + }); + subscriptions = await stripe.subscriptions.list({ customer: req.membershipOrg.organization.customerId }); diff --git a/backend/src/controllers/v1/passwordController.ts b/backend/src/controllers/v1/passwordController.ts index 63d2b9184..72a649bb1 100644 --- a/backend/src/controllers/v1/passwordController.ts +++ b/backend/src/controllers/v1/passwordController.ts @@ -1,3 +1,4 @@ +import infisical from 'infisical-node'; import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -7,7 +8,6 @@ import { User, BackupPrivateKey, LoginSRPDetail } from '../../models'; import { createToken } from '../../helpers/auth'; import { sendMail } from '../../helpers/nodemailer'; import { TokenService } from '../../services'; -import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, SITE_URL } from '../../config'; import { TOKEN_EMAIL_PASSWORD_RESET } from '../../variables'; import { BadRequestError } from '../../utils/errors'; @@ -44,7 +44,7 @@ export const emailPasswordReset = async (req: Request, res: Response) => { substitutions: { email, token, - callback_url: SITE_URL + '/password-reset' + callback_url: infisical.get('SITE_URL')! + '/password-reset' } }); } catch (err) { @@ -91,8 +91,8 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => { payload: { userId: user._id.toString() }, - expiresIn: JWT_SIGNUP_LIFETIME, - secret: JWT_SIGNUP_SECRET + expiresIn: infisical.get('JWT_SIGNUP_LIFETIME')!, + secret: infisical.get('JWT_SIGNUP_SECRET')! }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/controllers/v1/secretController.ts b/backend/src/controllers/v1/secretController.ts index c76e5e883..1d1f8981c 100644 --- a/backend/src/controllers/v1/secretController.ts +++ b/backend/src/controllers/v1/secretController.ts @@ -9,7 +9,7 @@ import { import { pushKeys } from '../../helpers/key'; import { eventPushSecrets } from '../../events'; import { EventService } from '../../services'; -import { postHogClient } from '../../services'; +import { getPostHogClient } from '../../services'; interface PushSecret { ciphertextKey: string; @@ -38,6 +38,7 @@ export const pushSecrets = async (req: Request, res: Response) => { // upload (encrypted) secrets to workspace with id [workspaceId] try { + const postHogClient = getPostHogClient(); let { secrets }: { secrets: PushSecret[] } = req.body; const { keys, environment, channel } = req.body; const { workspaceId } = req.params; @@ -111,6 +112,7 @@ export const pullSecrets = async (req: Request, res: Response) => { let secrets; let key; try { + const postHogClient = getPostHogClient(); const environment: string = req.query.environment as string; const channel: string = req.query.channel as string; const { workspaceId } = req.params; @@ -179,6 +181,7 @@ export const pullSecretsServiceToken = async (req: Request, res: Response) => { let secrets; let key; try { + const postHogClient = getPostHogClient(); const environment: string = req.query.environment as string; const channel: string = req.query.channel as string; const { workspaceId } = req.params; diff --git a/backend/src/controllers/v1/serviceTokenController.ts b/backend/src/controllers/v1/serviceTokenController.ts index 3fafb9043..76899a1a7 100644 --- a/backend/src/controllers/v1/serviceTokenController.ts +++ b/backend/src/controllers/v1/serviceTokenController.ts @@ -1,7 +1,7 @@ +import infisical from 'infisical-node'; import { Request, Response } from 'express'; import { ServiceToken } from '../../models'; import { createToken } from '../../helpers/auth'; -import { JWT_SERVICE_SECRET } from '../../config'; /** * Return service token on request @@ -61,7 +61,7 @@ export const createServiceToken = async (req: Request, res: Response) => { workspaceId }, expiresIn: expiresIn, - secret: JWT_SERVICE_SECRET + secret: infisical.get('JWT_SERVICE_SECRET')! }); } catch (err) { return res.status(400).send({ diff --git a/backend/src/controllers/v1/signupController.ts b/backend/src/controllers/v1/signupController.ts index 1c5c9e298..dbf5bd5a4 100644 --- a/backend/src/controllers/v1/signupController.ts +++ b/backend/src/controllers/v1/signupController.ts @@ -1,7 +1,7 @@ +import infisical from 'infisical-node'; 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 { sendEmailVerification, checkEmailVerification, @@ -21,7 +21,7 @@ export const beginEmailSignup = async (req: Request, res: Response) => { try { email = req.body.email; - if (INVITE_ONLY_SIGNUP) { + if (infisical.get('INVITE_ONLY_SIGNUP') || false) { // 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({}) if (userCount != 0) { @@ -91,8 +91,8 @@ export const verifyEmailSignup = async (req: Request, res: Response) => { payload: { userId: user._id.toString() }, - expiresIn: JWT_SIGNUP_LIFETIME, - secret: JWT_SIGNUP_SECRET + expiresIn: infisical.get('JWT_SIGNUP_LIFETIME')!, + secret: infisical.get('JWT_SIGNUP_SECRET')! }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/controllers/v1/stripeController.ts b/backend/src/controllers/v1/stripeController.ts index 15f62a056..1d3ee0bf2 100644 --- a/backend/src/controllers/v1/stripeController.ts +++ b/backend/src/controllers/v1/stripeController.ts @@ -1,10 +1,7 @@ +import infisical from 'infisical-node'; import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; import Stripe from 'stripe'; -import { STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET } from '../../config'; -const stripe = new Stripe(STRIPE_SECRET_KEY, { - apiVersion: '2022-08-01' -}); /** * Handle service provisioning/un-provisioning via Stripe @@ -16,11 +13,15 @@ export const handleWebhook = async (req: Request, res: Response) => { let event; try { // check request for valid stripe signature + const stripe = new Stripe(infisical.get('STRIPE_SECRET_KEY')!, { + apiVersion: '2022-08-01' + }); + const sig = req.headers['stripe-signature'] as string; event = stripe.webhooks.constructEvent( req.body, sig, - STRIPE_WEBHOOK_SECRET // ? + infisical.get('STRIPE_WEBHOOK_SECRET')! ); } catch (err) { Sentry.setUser({ email: req.user.email }); diff --git a/backend/src/controllers/v2/apiKeyDataController.ts b/backend/src/controllers/v2/apiKeyDataController.ts index cafbacb5b..b6819928a 100644 --- a/backend/src/controllers/v2/apiKeyDataController.ts +++ b/backend/src/controllers/v2/apiKeyDataController.ts @@ -1,13 +1,11 @@ -import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; +import infisical from 'infisical-node'; +import { Request, Response } from 'express'; import crypto from 'crypto'; import bcrypt from 'bcrypt'; import { APIKeyData } from '../../models'; -import { - SALT_ROUNDS -} from '../../config'; /** * Return API key data for user with id [req.user_id] @@ -45,7 +43,7 @@ export const createAPIKeyData = async (req: Request, res: Response) => { const { name, expiresIn } = req.body; const secret = crypto.randomBytes(16).toString('hex'); - const secretHash = await bcrypt.hash(secret, SALT_ROUNDS); + const secretHash = await bcrypt.hash(secret, parseInt(infisical.get('SALT_ROUNDS')!) || 10); const expiresAt = new Date(); expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn); diff --git a/backend/src/controllers/v2/authController.ts b/backend/src/controllers/v2/authController.ts index 95a1613d2..4401e2498 100644 --- a/backend/src/controllers/v2/authController.ts +++ b/backend/src/controllers/v2/authController.ts @@ -1,3 +1,4 @@ +import infisical from 'infisical-node'; /* eslint-disable @typescript-eslint/no-var-requires */ import { Request, Response } from 'express'; import jwt from 'jsonwebtoken'; @@ -10,11 +11,6 @@ import { checkUserDevice } from '../../helpers/user'; import { sendMail } from '../../helpers/nodemailer'; import { TokenService } from '../../services'; import { EELogService } from '../../ee/services'; -import { - NODE_ENV, - JWT_MFA_LIFETIME, - JWT_MFA_SECRET -} from '../../config'; import { BadRequestError, InternalServerError } from '../../utils/errors'; import { TOKEN_EMAIL_MFA, @@ -28,8 +24,6 @@ declare module 'jsonwebtoken' { } } -const clientPublicKeys: any = {}; - /** * Log in user step 1: Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol * @param req @@ -126,8 +120,8 @@ export const login2 = async (req: Request, res: Response) => { payload: { userId: user._id.toString() }, - expiresIn: JWT_MFA_LIFETIME, - secret: JWT_MFA_SECRET + expiresIn: infisical.get('JWT_MFA_LIFETIME')!, + secret: infisical.get('JWT_MFA_SECRET')! }); const code = await TokenService.createToken({ @@ -165,7 +159,7 @@ export const login2 = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: NODE_ENV === 'production' ? true : false + secure: infisical.get('NODE_ENV')! === 'production' ? true : false }); // case: user does not have MFA enablgged @@ -304,7 +298,7 @@ export const verifyMfaToken = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: NODE_ENV === 'production' ? true : false + secure: infisical.get('NODE_ENV')! === 'production' ? true : false }); interface VerifyMfaTokenRes { diff --git a/backend/src/controllers/v2/secretController.ts b/backend/src/controllers/v2/secretController.ts index 89567e616..cd91dca67 100644 --- a/backend/src/controllers/v2/secretController.ts +++ b/backend/src/controllers/v2/secretController.ts @@ -7,7 +7,7 @@ const { ValidationError } = mongoose.Error; import { BadRequestError, InternalServerError, UnauthorizedRequestError, ValidationError as RouteValidationError } from '../../utils/errors'; import { AnyBulkWriteOperation } from 'mongodb'; import { SECRET_PERSONAL, SECRET_SHARED } from "../../variables"; -import { postHogClient } from '../../services'; +import { getPostHogClient } from '../../services'; /** * Create secret for workspace with id [workspaceId] and environment [environment] @@ -15,6 +15,7 @@ import { postHogClient } from '../../services'; * @param res */ export const createSecret = async (req: Request, res: Response) => { + const postHogClient = getPostHogClient(); const secretToCreate: CreateSecretRequestBody = req.body.secret; const { workspaceId, environment } = req.params const sanitizedSecret: SanitizedSecretForCreate = { @@ -67,6 +68,7 @@ export const createSecret = async (req: Request, res: Response) => { * @param res */ export const createSecrets = async (req: Request, res: Response) => { + const postHogClient = getPostHogClient(); const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets; const { workspaceId, environment } = req.params const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = [] @@ -128,6 +130,7 @@ export const createSecrets = async (req: Request, res: Response) => { * @param res */ export const deleteSecrets = async (req: Request, res: Response) => { + const postHogClient = getPostHogClient(); const { workspaceId, environmentName } = req.params const secretIdsToDelete: string[] = req.body.secretIds @@ -181,6 +184,7 @@ export const deleteSecrets = async (req: Request, res: Response) => { * @param res */ export const deleteSecret = async (req: Request, res: Response) => { + const postHogClient = getPostHogClient(); await Secret.findByIdAndDelete(req._secret._id) if (postHogClient) { @@ -209,6 +213,7 @@ export const deleteSecret = async (req: Request, res: Response) => { * @returns */ export const updateSecrets = async (req: Request, res: Response) => { + const postHogClient = getPostHogClient(); const { workspaceId, environmentName } = req.params const secretsModificationsRequested: ModifySecretRequestBody[] = req.body.secrets; const [secretIdsUserCanModifyError, secretIdsUserCanModify] = await to(Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then()) @@ -276,6 +281,7 @@ export const updateSecrets = async (req: Request, res: Response) => { * @returns */ export const updateSecret = async (req: Request, res: Response) => { + const postHogClient = getPostHogClient(); const { workspaceId, environmentName } = req.params const secretModificationsRequested: ModifySecretRequestBody = req.body.secret; @@ -329,6 +335,7 @@ export const updateSecret = async (req: Request, res: Response) => { * @returns */ export const getSecrets = async (req: Request, res: Response) => { + const postHogClient = getPostHogClient(); const { environment } = req.query; const { workspaceId } = req.params; diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts index 486bf017e..66aecd423 100644 --- a/backend/src/controllers/v2/secretsController.ts +++ b/backend/src/controllers/v2/secretsController.ts @@ -15,7 +15,7 @@ import { UnauthorizedRequestError, ValidationError } from '../../utils/errors'; import { EventService } from '../../services'; import { eventPushSecrets } from '../../events'; import { EESecretService, EELogService } from '../../ee/services'; -import { postHogClient } from '../../services'; +import { getPostHogClient } from '../../services'; import { getChannelFromUserAgent } from '../../utils/posthog'; import { ABILITY_READ, ABILITY_WRITE } from '../../variables/organization'; import { userHasNoAbility, userHasWorkspaceAccess, userHasWriteOnlyAbility } from '../../ee/helpers/checkMembershipPermissions'; @@ -33,6 +33,8 @@ import { */ export const batchSecrets = async (req: Request, res: Response) => { const channel = getChannelFromUserAgent(req.headers['user-agent']); + const postHogClient = getPostHogClient(); + const { workspaceId, environment, @@ -326,6 +328,7 @@ export const createSecrets = async (req: Request, res: Response) => { } } */ + const postHogClient = getPostHogClient(); const channel = getChannelFromUserAgent(req.headers['user-agent']) const { workspaceId, environment }: { workspaceId: string, environment: string } = req.body; @@ -530,6 +533,7 @@ export const getSecrets = async (req: Request, res: Response) => { } */ + const postHogClient = getPostHogClient(); const { workspaceId, environment, tagSlugs } = req.query; const tagNamesList = typeof tagSlugs === 'string' && tagSlugs !== '' ? tagSlugs.split(',') : []; @@ -732,6 +736,7 @@ export const updateSecrets = async (req: Request, res: Response) => { } } */ + const postHogClient = getPostHogClient(); const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; // TODO: move type @@ -953,7 +958,7 @@ export const deleteSecrets = async (req: Request, res: Response) => { } } */ - + const postHogClient = getPostHogClient(); const channel = getChannelFromUserAgent(req.headers['user-agent']) const toDelete = req.secrets.map((s: any) => s._id); diff --git a/backend/src/controllers/v2/serviceTokenDataController.ts b/backend/src/controllers/v2/serviceTokenDataController.ts index cabedabea..295664cf9 100644 --- a/backend/src/controllers/v2/serviceTokenDataController.ts +++ b/backend/src/controllers/v2/serviceTokenDataController.ts @@ -1,13 +1,11 @@ -import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; +import infisical from 'infisical-node'; +import { Request, Response } from 'express'; import crypto from 'crypto'; import bcrypt from 'bcrypt'; import { ServiceTokenData } from '../../models'; -import { - SALT_ROUNDS -} from '../../config'; import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissions'; import { ABILITY_READ } from '../../variables/organization'; @@ -75,7 +73,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => { } const secret = crypto.randomBytes(16).toString('hex'); - const secretHash = await bcrypt.hash(secret, SALT_ROUNDS); + const secretHash = await bcrypt.hash(secret, parseInt(infisical.get('SALT_ROUNDS')!) || 10); const expiresAt = new Date(); expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn); diff --git a/backend/src/controllers/v2/signupController.ts b/backend/src/controllers/v2/signupController.ts index 79cb6730d..0eb148e37 100644 --- a/backend/src/controllers/v2/signupController.ts +++ b/backend/src/controllers/v2/signupController.ts @@ -1,3 +1,4 @@ +import infisical from 'infisical-node'; import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; import { User, MembershipOrg } from '../../models'; @@ -7,7 +8,6 @@ import { } from '../../helpers/signup'; import { issueAuthTokens } from '../../helpers/auth'; import { INVITED, ACCEPTED } from '../../variables'; -import { NODE_ENV } from '../../config'; import request from '../../config/request'; /** @@ -127,7 +127,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: NODE_ENV === 'production' ? true : false + secure: infisical.get('NODE_ENV')! === 'production' ? true : false }); } catch (err) { Sentry.setUser(null); @@ -232,7 +232,7 @@ export const completeAccountInvite = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: NODE_ENV === 'production' ? true : false + secure: infisical.get('NODE_ENV')! === 'production' ? true : false }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/controllers/v2/workspaceController.ts b/backend/src/controllers/v2/workspaceController.ts index 55cd02fff..650c70610 100644 --- a/backend/src/controllers/v2/workspaceController.ts +++ b/backend/src/controllers/v2/workspaceController.ts @@ -19,7 +19,7 @@ import { reformatPullSecrets } from '../../helpers/secret'; import { pushKeys } from '../../helpers/key'; -import { postHogClient, EventService } from '../../services'; +import { getPostHogClient, EventService } from '../../services'; import { eventPushSecrets } from '../../events'; interface V2PushSecret { @@ -48,6 +48,7 @@ interface V2PushSecret { export const pushWorkspaceSecrets = async (req: Request, res: Response) => { // upload (encrypted) secrets to workspace with id [workspaceId] try { + const postHogClient = getPostHogClient(); let { secrets }: { secrets: V2PushSecret[] } = req.body; const { keys, environment, channel } = req.body; const { workspaceId } = req.params; @@ -121,6 +122,7 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => { export const pullSecrets = async (req: Request, res: Response) => { let secrets; try { + const postHogClient = getPostHogClient(); const environment: string = req.query.environment as string; const channel: string = req.query.channel as string; const { workspaceId } = req.params; diff --git a/backend/src/ee/controllers/v1/stripeController.ts b/backend/src/ee/controllers/v1/stripeController.ts index faef14cea..7c816550e 100644 --- a/backend/src/ee/controllers/v1/stripeController.ts +++ b/backend/src/ee/controllers/v1/stripeController.ts @@ -1,10 +1,7 @@ -import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; +import infisical from 'infisical-node'; +import { Request, Response } from 'express'; import Stripe from 'stripe'; -import { STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET } from '../../../config'; -const stripe = new Stripe(STRIPE_SECRET_KEY, { - apiVersion: '2022-08-01' -}); /** * Handle service provisioning/un-provisioning via Stripe @@ -15,12 +12,16 @@ const stripe = new Stripe(STRIPE_SECRET_KEY, { export const handleWebhook = async (req: Request, res: Response) => { let event; try { + const stripe = new Stripe(infisical.get('STRIPE_SECRET_KEY')!, { + apiVersion: '2022-08-01' + }); + // check request for valid stripe signature const sig = req.headers['stripe-signature'] as string; event = stripe.webhooks.constructEvent( req.body, sig, - STRIPE_WEBHOOK_SECRET // ? + infisical.get('STRIPE_WEBHOOK_SECRET')! ); } catch (err) { Sentry.setUser({ email: req.user.email }); diff --git a/backend/src/ee/services/EELicenseService.ts b/backend/src/ee/services/EELicenseService.ts index f31482dde..4bd811340 100644 --- a/backend/src/ee/services/EELicenseService.ts +++ b/backend/src/ee/services/EELicenseService.ts @@ -1,5 +1,3 @@ -import { LICENSE_KEY } from '../../config'; - /** * Class to handle Enterprise Edition license actions */ @@ -16,4 +14,4 @@ class EELicenseService { } } -export default new EELicenseService(LICENSE_KEY); \ No newline at end of file +export default new EELicenseService('N/A'); \ No newline at end of file diff --git a/backend/src/helpers/auth.ts b/backend/src/helpers/auth.ts index 102a7ac07..680120395 100644 --- a/backend/src/helpers/auth.ts +++ b/backend/src/helpers/auth.ts @@ -1,5 +1,6 @@ -import jwt from 'jsonwebtoken'; import * as Sentry from '@sentry/node'; +import infisical from 'infisical-node'; +import jwt from 'jsonwebtoken'; import bcrypt from 'bcrypt'; import { IUser, @@ -7,12 +8,6 @@ import { ServiceTokenData, APIKeyData } from '../models'; -import { - JWT_AUTH_LIFETIME, - JWT_AUTH_SECRET, - JWT_REFRESH_LIFETIME, - JWT_REFRESH_SECRET -} from '../config'; import { AccountNotFoundError, ServiceTokenDataNotFoundError, @@ -93,7 +88,7 @@ const getAuthUserPayload = async ({ let user; try { const decodedToken = ( - jwt.verify(authTokenValue, JWT_AUTH_SECRET) + jwt.verify(authTokenValue, infisical.get('JWT_AUTH_SECRET')!) ); user = await User.findOne({ @@ -224,16 +219,16 @@ const issueAuthTokens = async ({ userId }: { userId: string }) => { payload: { userId }, - expiresIn: JWT_AUTH_LIFETIME, - secret: JWT_AUTH_SECRET + expiresIn: infisical.get('JWT_AUTH_LIFETIME')!, + secret: infisical.get('JWT_AUTH_SECRET')! }); refreshToken = createToken({ payload: { userId }, - expiresIn: JWT_REFRESH_LIFETIME, - secret: JWT_REFRESH_SECRET + expiresIn: infisical.get('JWT_REFRESH_LIFETIME')!, + secret: infisical.get('JWT_REFRESH_SECRET')! }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/helpers/bot.ts b/backend/src/helpers/bot.ts index 7519ef18b..8912befd1 100644 --- a/backend/src/helpers/bot.ts +++ b/backend/src/helpers/bot.ts @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/node'; +import infisical from 'infisical-node'; import { Bot, BotKey, @@ -12,7 +13,6 @@ import { decryptSymmetric, decryptAsymmetric } from '../utils/crypto'; -import { ENCRYPTION_KEY } from '../config'; import { SECRET_SHARED } from '../variables'; /** @@ -33,7 +33,7 @@ const createBot = async ({ const { publicKey, privateKey } = generateKeyPair(); const { ciphertext, iv, tag } = encryptSymmetric({ plaintext: privateKey, - key: ENCRYPTION_KEY + key: infisical.get('ENCRYPTION_KEY')! }); bot = await new Bot({ @@ -130,7 +130,7 @@ const getKey = async ({ workspaceId }: { workspaceId: string }) => { ciphertext: bot.encryptedPrivateKey, iv: bot.iv, tag: bot.tag, - key: ENCRYPTION_KEY + key: infisical.get('ENCRYPTION_KEY')! }); key = decryptAsymmetric({ diff --git a/backend/src/helpers/nodemailer.ts b/backend/src/helpers/nodemailer.ts index 958342aae..45fce5d1d 100644 --- a/backend/src/helpers/nodemailer.ts +++ b/backend/src/helpers/nodemailer.ts @@ -1,9 +1,9 @@ +import * as Sentry from '@sentry/node'; +import infisical from 'infisical-node'; 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 * as Sentry from '@sentry/node'; let smtpTransporter: nodemailer.Transporter; @@ -34,7 +34,7 @@ const sendMail = async ({ const htmlToSend = temp(substitutions); await smtpTransporter.sendMail({ - from: `"${SMTP_FROM_NAME}" <${SMTP_FROM_ADDRESS}>`, + from: `"${infisical.get('SMTP_FROM_NAME')!}" <${infisical.get('SMTP_FROM_ADDRESS')!}>`, to: recipients.join(', '), subject: subjectLine, html: htmlToSend diff --git a/backend/src/helpers/organization.ts b/backend/src/helpers/organization.ts index 4ba3592de..87711da92 100644 --- a/backend/src/helpers/organization.ts +++ b/backend/src/helpers/organization.ts @@ -1,24 +1,10 @@ +import infisical from 'infisical-node'; import * as Sentry from '@sentry/node'; import Stripe from 'stripe'; -import { - STRIPE_SECRET_KEY, - STRIPE_PRODUCT_STARTER, - STRIPE_PRODUCT_TEAM, - STRIPE_PRODUCT_PRO -} from '../config'; -const stripe = new Stripe(STRIPE_SECRET_KEY, { - apiVersion: '2022-08-01' -}); import { Types } from 'mongoose'; import { ACCEPTED } from '../variables'; import { Organization, MembershipOrg } from '../models'; -const productToPriceMap = { - starter: STRIPE_PRODUCT_STARTER, - team: STRIPE_PRODUCT_TEAM, - pro: STRIPE_PRODUCT_PRO -}; - /** * Create an organization with name [name] * @param {Object} obj @@ -36,8 +22,11 @@ const createOrganization = async ({ let organization; try { // register stripe account + const stripe = new Stripe(infisical.get('STRIPE_SECRET_KEY')!, { + apiVersion: '2022-08-01' + }); - if (STRIPE_SECRET_KEY) { + if (infisical.get('STRIPE_SECRET_KEY')) { const customer = await stripe.customers.create({ email, description: name @@ -87,6 +76,16 @@ const initSubscriptionOrg = async ({ if (organization) { if (organization.customerId) { // initialize starter subscription with quantity of 0 + const stripe = new Stripe(infisical.get('STRIPE_SECRET_KEY')!, { + apiVersion: '2022-08-01' + }); + + const productToPriceMap = { + starter: infisical.get('STRIPE_PRODUCT_STARTER')!, + team: infisical.get('STRIPE_PRODUCT_TEAM')!, + pro: infisical.get('STRIPE_PRODUCT_PRO')! + }; + stripeSubscription = await stripe.subscriptions.create({ customer: organization.customerId, items: [ @@ -139,6 +138,10 @@ const updateSubscriptionOrgQuantity = async ({ status: ACCEPTED }); + const stripe = new Stripe(infisical.get('STRIPE_SECRET_KEY')!, { + apiVersion: '2022-08-01' + }); + const subscription = ( await stripe.subscriptions.list({ customer: organization.customerId diff --git a/backend/src/helpers/token.ts b/backend/src/helpers/token.ts index ca8838151..b2365cb2c 100644 --- a/backend/src/helpers/token.ts +++ b/backend/src/helpers/token.ts @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/node'; +import infisical from 'infisical-node'; import { Types } from 'mongoose'; import { TokenData } from '../models'; import crypto from 'crypto'; @@ -9,9 +10,6 @@ import { TOKEN_EMAIL_ORG_INVITATION, TOKEN_EMAIL_PASSWORD_RESET } from '../variables'; -import { - SALT_ROUNDS -} from '../config'; import { UnauthorizedRequestError } from '../utils/errors'; /** @@ -86,7 +84,7 @@ const createTokenHelper = async ({ const query: TokenDataQuery = { type }; const update: TokenDataUpdate = { type, - tokenHash: await bcrypt.hash(token, SALT_ROUNDS), + tokenHash: await bcrypt.hash(token, parseInt(infisical.get('SALT_ROUNDS')!) || 10), expiresAt } diff --git a/backend/src/index.ts b/backend/src/index.ts index a97b5b33b..24172fc89 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,28 +1,168 @@ import dotenv from 'dotenv'; dotenv.config(); - +import infisical from 'infisical-node'; +import express from 'express'; +import helmet from 'helmet'; +import cors from 'cors'; import * as Sentry from '@sentry/node'; -import { SENTRY_DSN, NODE_ENV, MONGO_URL } from './config'; -import { server } from './app'; import { DatabaseService } from './services'; import { setUpHealthEndpoint } from './services/health'; import { initSmtp } from './services/smtp'; +import { logTelemetryMessage } from './services'; import { setTransporter } from './helpers/nodemailer'; import { createTestUserForDevelopment } from './utils/addDevelopmentUser'; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { patchRouterParam } = require('./utils/patchAsyncRoutes'); -DatabaseService.initDatabase(MONGO_URL); +import cookieParser from 'cookie-parser'; +import swaggerUi = require('swagger-ui-express'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const swaggerFile = require('../spec.json'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const requestIp = require('request-ip'); +import { apiLimiter } from './helpers/rateLimiter'; +import { + workspace as eeWorkspaceRouter, + secret as eeSecretRouter, + secretSnapshot as eeSecretSnapshotRouter, + action as eeActionRouter +} from './ee/routes/v1'; +import { + signup as v1SignupRouter, + auth as v1AuthRouter, + bot as v1BotRouter, + organization as v1OrganizationRouter, + workspace as v1WorkspaceRouter, + membershipOrg as v1MembershipOrgRouter, + membership as v1MembershipRouter, + key as v1KeyRouter, + inviteOrg as v1InviteOrgRouter, + user as v1UserRouter, + userAction as v1UserActionRouter, + secret as v1SecretRouter, + serviceToken as v1ServiceTokenRouter, + password as v1PasswordRouter, + stripe as v1StripeRouter, + integration as v1IntegrationRouter, + integrationAuth as v1IntegrationAuthRouter +} from './routes/v1'; +import { + signup as v2SignupRouter, + auth as v2AuthRouter, + users as v2UsersRouter, + organizations as v2OrganizationsRouter, + workspace as v2WorkspaceRouter, + secret as v2SecretRouter, // begin to phase out + secrets as v2SecretsRouter, + serviceTokenData as v2ServiceTokenDataRouter, + apiKeyData as v2APIKeyDataRouter, + environment as v2EnvironmentRouter, + tags as v2TagsRouter, +} from './routes/v2'; +import { healthCheck } from './routes/status'; +import { getLogger } from './utils/logger'; +import { RouteNotFoundError } from './utils/errors'; +import { requestErrorHandler } from './middleware/requestErrorHandler'; -setUpHealthEndpoint(server); +const main = async () => { + const client = await infisical.connect({ + token: process.env.INFISICAL_TOKEN!, + debug: true + }); + + logTelemetryMessage(); + setTransporter(initSmtp()); -setTransporter(initSmtp()); + await DatabaseService.initDatabase(infisical.get('MONGO_URL')!); + if (infisical.get('NODE_ENV') !== 'test') { + Sentry.init({ + dsn: infisical.get('SENTRY_DSN') as string, + tracesSampleRate: 1.0, + debug: infisical.get('NODE_ENV') === 'production' ? false : true, + environment: infisical.get('NODE_ENV') as string + }); + } -if (NODE_ENV !== 'test') { - Sentry.init({ - dsn: SENTRY_DSN, - tracesSampleRate: 1.0, - debug: NODE_ENV === 'production' ? false : true, - environment: NODE_ENV - }); + patchRouterParam(); + const app = express(); + app.enable('trust proxy'); + app.use(express.json()); + app.use(cookieParser()); + app.use( + cors({ + credentials: true, + origin: infisical.get('SITE_URL') as string + }) + ); + + app.use(requestIp.mw()); + + if (infisical.get('NODE_ENV') === 'production') { + // enable app-wide rate-limiting + helmet security + // in production + app.disable('x-powered-by'); + app.use(apiLimiter); + app.use(helmet()); + } + + // (EE) routes + app.use('/api/v1/secret', eeSecretRouter); + app.use('/api/v1/secret-snapshot', eeSecretSnapshotRouter); + app.use('/api/v1/workspace', eeWorkspaceRouter); + app.use('/api/v1/action', eeActionRouter); + + // v1 routes + app.use('/api/v1/signup', v1SignupRouter); + app.use('/api/v1/auth', v1AuthRouter); + app.use('/api/v1/bot', v1BotRouter); + app.use('/api/v1/user', v1UserRouter); + app.use('/api/v1/user-action', v1UserActionRouter); + app.use('/api/v1/organization', v1OrganizationRouter); + app.use('/api/v1/workspace', v1WorkspaceRouter); + app.use('/api/v1/membership-org', v1MembershipOrgRouter); + app.use('/api/v1/membership', v1MembershipRouter); + app.use('/api/v1/key', v1KeyRouter); + app.use('/api/v1/invite-org', v1InviteOrgRouter); + app.use('/api/v1/secret', v1SecretRouter); + app.use('/api/v1/service-token', v1ServiceTokenRouter); // deprecated + app.use('/api/v1/password', v1PasswordRouter); + app.use('/api/v1/stripe', v1StripeRouter); + app.use('/api/v1/integration', v1IntegrationRouter); + app.use('/api/v1/integration-auth', v1IntegrationAuthRouter); + + // v2 routes + app.use('/api/v2/signup', v2SignupRouter); + app.use('/api/v2/auth', v2AuthRouter); + app.use('/api/v2/users', v2UsersRouter); + app.use('/api/v2/organizations', v2OrganizationsRouter); + app.use('/api/v2/workspace', v2EnvironmentRouter); + app.use('/api/v2/workspace', v2TagsRouter); + app.use('/api/v2/workspace', v2WorkspaceRouter); + app.use('/api/v2/secret', v2SecretRouter); // deprecated + app.use('/api/v2/secrets', v2SecretsRouter); + app.use('/api/v2/service-token', v2ServiceTokenDataRouter); // TODO: turn into plural route + app.use('/api/v2/api-key', v2APIKeyDataRouter); + + // api docs + app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerFile)) + + // Server status + app.use('/api', healthCheck) + + //* Handle unrouted requests and respond with proper error message as well as status code + app.use((req, res, next) => { + if (res.headersSent) return next(); + next(RouteNotFoundError({ message: `The requested source '(${req.method})${req.url}' was not found` })) + }) + + app.use(requestErrorHandler) + + const server = app.listen(Number(infisical.get('PORT')) || 4000, () => { + createTestUserForDevelopment(); + getLogger("backend-main").info(`Server started listening at port ${Number(infisical.get('PORT')) || 4000}`) + }); + + setUpHealthEndpoint(server); } -createTestUserForDevelopment() +main(); \ No newline at end of file diff --git a/backend/src/integrations/exchange.ts b/backend/src/integrations/exchange.ts index 224979c88..4e89e3aa9 100644 --- a/backend/src/integrations/exchange.ts +++ b/backend/src/integrations/exchange.ts @@ -1,5 +1,6 @@ -import request from '../config/request'; +import infisical from 'infisical-node'; import * as Sentry from '@sentry/node'; +import request from '../config/request'; import { INTEGRATION_AZURE_KEY_VAULT, INTEGRATION_HEROKU, @@ -14,20 +15,6 @@ import { INTEGRATION_GITHUB_TOKEN_URL, INTEGRATION_GITLAB_TOKEN_URL } from '../variables'; -import { - SITE_URL, - CLIENT_ID_AZURE, - CLIENT_ID_VERCEL, - CLIENT_ID_NETLIFY, - CLIENT_ID_GITHUB, - CLIENT_ID_GITLAB, - CLIENT_SECRET_AZURE, - CLIENT_SECRET_HEROKU, - CLIENT_SECRET_VERCEL, - CLIENT_SECRET_NETLIFY, - CLIENT_SECRET_GITHUB, - CLIENT_SECRET_GITLAB, -} from '../config'; interface ExchangeCodeAzureResponse { token_type: string; @@ -159,9 +146,9 @@ const exchangeCodeAzure = async ({ grant_type: 'authorization_code', code: code, scope: 'https://vault.azure.net/.default openid offline_access', - client_id: CLIENT_ID_AZURE, - client_secret: CLIENT_SECRET_AZURE, - redirect_uri: `${SITE_URL}/integrations/azure-key-vault/oauth2/callback` + client_id: infisical.get('CLIENT_ID_AZURE')!, + client_secret: infisical.get('CLIENT_SECRET_AZURE')!, + redirect_uri: `${infisical.get('SITE_URL')!}/integrations/azure-key-vault/oauth2/callback` } as any) )).data; @@ -204,7 +191,7 @@ const exchangeCodeHeroku = async ({ new URLSearchParams({ grant_type: 'authorization_code', code: code, - client_secret: CLIENT_SECRET_HEROKU + client_secret: infisical.get('CLIENT_SECRET_HEROKU')! } as any) )).data; @@ -242,9 +229,9 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => { INTEGRATION_VERCEL_TOKEN_URL, new URLSearchParams({ code: code, - client_id: CLIENT_ID_VERCEL, - client_secret: CLIENT_SECRET_VERCEL, - redirect_uri: `${SITE_URL}/integrations/vercel/oauth2/callback` + client_id: infisical.get('CLIENT_ID_VERCEL')!, + client_secret: infisical.get('CLIENT_SECRET_VERCEL')!, + redirect_uri: `${infisical.get('SITE_URL')!}/integrations/vercel/oauth2/callback` } as any) ) ).data; @@ -282,9 +269,9 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => { new URLSearchParams({ grant_type: 'authorization_code', code: code, - client_id: CLIENT_ID_NETLIFY, - client_secret: CLIENT_SECRET_NETLIFY, - redirect_uri: `${SITE_URL}/integrations/netlify/oauth2/callback` + client_id: infisical.get('CLIENT_ID_NETLIFY')!, + client_secret: infisical.get('CLIENT_SECRET_NETLIFY')!, + redirect_uri: `${infisical.get('SITE_URL')!}/integrations/netlify/oauth2/callback` } as any) ) ).data; @@ -333,10 +320,10 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => { res = ( await request.get(INTEGRATION_GITHUB_TOKEN_URL, { params: { - client_id: CLIENT_ID_GITHUB, - client_secret: CLIENT_SECRET_GITHUB, + client_id: infisical.get('CLIENT_ID_GITHUB')!, + client_secret: infisical.get('CLIENT_SECRET_GITHUB')!, code: code, - redirect_uri: `${SITE_URL}/integrations/github/oauth2/callback` + redirect_uri: `${infisical.get('SITE_URL')!}/integrations/github/oauth2/callback` }, headers: { 'Accept': 'application/json', @@ -379,9 +366,9 @@ const exchangeCodeGitlab = async ({ code }: { code: string }) => { new URLSearchParams({ grant_type: 'authorization_code', code: code, - client_id: CLIENT_ID_GITLAB, - client_secret: CLIENT_SECRET_GITLAB, - redirect_uri: `${SITE_URL}/integrations/gitlab/oauth2/callback` + client_id: infisical.get('CLIENT_ID_GITLAB')!, + client_secret: infisical.get('CLIENT_SECRET_GITLAB')!, + redirect_uri: `${infisical.get('SITE_URL')}/integrations/gitlab/oauth2/callback` } as any), { headers: { diff --git a/backend/src/integrations/refresh.ts b/backend/src/integrations/refresh.ts index a3aec1ebd..67cd382c1 100644 --- a/backend/src/integrations/refresh.ts +++ b/backend/src/integrations/refresh.ts @@ -1,5 +1,6 @@ -import request from '../config/request'; +import infisical from 'infisical-node'; import * as Sentry from '@sentry/node'; +import request from '../config/request'; import { IIntegrationAuth } from '../models'; @@ -8,14 +9,13 @@ import { INTEGRATION_HEROKU, INTEGRATION_GITLAB, } from '../variables'; -import { - SITE_URL, - CLIENT_ID_AZURE, - CLIENT_ID_GITLAB, - CLIENT_SECRET_AZURE, - CLIENT_SECRET_HEROKU, - CLIENT_SECRET_GITLAB -} from '../config'; +// import { +// CLIENT_ID_AZURE, +// CLIENT_ID_GITLAB, +// CLIENT_SECRET_AZURE, +// CLIENT_SECRET_HEROKU, +// CLIENT_SECRET_GITLAB +// } from '../config'; import { INTEGRATION_AZURE_TOKEN_URL, INTEGRATION_HEROKU_TOKEN_URL, @@ -133,11 +133,11 @@ const exchangeRefreshAzure = async ({ const { data }: { data: RefreshTokenAzureResponse } = await request.post( INTEGRATION_AZURE_TOKEN_URL, new URLSearchParams({ - client_id: CLIENT_ID_AZURE, + client_id: infisical.get('CLIENT_ID_AZURE')!, scope: 'openid offline_access', refresh_token: refreshToken, grant_type: 'refresh_token', - client_secret: CLIENT_SECRET_AZURE + client_secret: infisical.get('CLIENT_SECRET_AZURE')! } as any) ); @@ -180,7 +180,7 @@ const exchangeRefreshHeroku = async ({ new URLSearchParams({ grant_type: 'refresh_token', refresh_token: refreshToken, - client_secret: CLIENT_SECRET_HEROKU + client_secret: infisical.get('CLIENT_SECRET_HEROKU')! } as any) ); @@ -223,9 +223,9 @@ const exchangeRefreshGitLab = async ({ new URLSearchParams({ grant_type: 'refresh_token', refresh_token: refreshToken, - client_id: CLIENT_ID_GITLAB, - client_secret: CLIENT_SECRET_GITLAB, - redirect_uri: `${SITE_URL}/integrations/gitlab/oauth2/callback` + client_id: infisical.get('CLIENT_ID_GITLAB')!, + client_secret: infisical.get('CLIENT_SECRET_GITLAB')!, + redirect_uri: `${infisical.get('SITE_URL')!}/integrations/gitlab/oauth2/callback` } as any), { headers: { diff --git a/backend/src/middleware/requestErrorHandler.ts b/backend/src/middleware/requestErrorHandler.ts index 653c0df2e..2a881e814 100644 --- a/backend/src/middleware/requestErrorHandler.ts +++ b/backend/src/middleware/requestErrorHandler.ts @@ -1,16 +1,13 @@ -import { ErrorRequestHandler } from "express"; - +import infisical from 'infisical-node'; import * as Sentry from '@sentry/node'; +import { ErrorRequestHandler } from "express"; import { InternalServerError, UnauthorizedRequestError } from "../utils/errors"; import { getLogger } from "../utils/logger"; import RequestError, { LogLevel } from "../utils/requestError"; -import { NODE_ENV } from "../config"; - -import { TokenExpiredError } from 'jsonwebtoken'; export const requestErrorHandler: ErrorRequestHandler = (error: RequestError | Error, req, res, next) => { if (res.headersSent) return next(); - if (NODE_ENV !== "production") { + if (infisical.get('NODE_ENV')! !== "production") { /* eslint-disable no-console */ console.log(error) /* eslint-enable no-console */ diff --git a/backend/src/middleware/requireMfaAuth.ts b/backend/src/middleware/requireMfaAuth.ts index 8fb914258..21b4763c2 100644 --- a/backend/src/middleware/requireMfaAuth.ts +++ b/backend/src/middleware/requireMfaAuth.ts @@ -1,7 +1,7 @@ +import infisical from 'infisical-node'; import jwt from 'jsonwebtoken'; import { Request, Response, NextFunction } from 'express'; import { User } from '../models'; -import { JWT_MFA_SECRET } from '../config'; import { BadRequestError, UnauthorizedRequestError } from '../utils/errors'; declare module 'jsonwebtoken' { @@ -26,7 +26,7 @@ const requireMfaAuth = async ( if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'})) const decodedToken = ( - jwt.verify(AUTH_TOKEN_VALUE, JWT_MFA_SECRET) + jwt.verify(AUTH_TOKEN_VALUE, infisical.get('JWT_MFA_SECRET')!) ); const user = await User.findOne({ diff --git a/backend/src/middleware/requireServiceTokenAuth.ts b/backend/src/middleware/requireServiceTokenAuth.ts index 904f4d38e..ff6bb7c59 100644 --- a/backend/src/middleware/requireServiceTokenAuth.ts +++ b/backend/src/middleware/requireServiceTokenAuth.ts @@ -1,7 +1,7 @@ +import infisical from 'infisical-node'; import jwt from 'jsonwebtoken'; import { Request, Response, NextFunction } from 'express'; import { ServiceToken } from '../models'; -import { JWT_SERVICE_SECRET } from '../config'; import { BadRequestError, UnauthorizedRequestError } from '../utils/errors'; // TODO: deprecate @@ -33,7 +33,7 @@ const requireServiceTokenAuth = async ( if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'})) const decodedToken = ( - jwt.verify(AUTH_TOKEN_VALUE, JWT_SERVICE_SECRET) + jwt.verify(AUTH_TOKEN_VALUE, infisical.get('JWT_SERVICE_SECRET')!) ); const serviceToken = await ServiceToken.findOne({ diff --git a/backend/src/middleware/requireSignupAuth.ts b/backend/src/middleware/requireSignupAuth.ts index 3318bd8d3..deb08d9ae 100644 --- a/backend/src/middleware/requireSignupAuth.ts +++ b/backend/src/middleware/requireSignupAuth.ts @@ -1,7 +1,7 @@ +import infisical from 'infisical-node'; import jwt from 'jsonwebtoken'; import { Request, Response, NextFunction } from 'express'; import { User } from '../models'; -import { JWT_SIGNUP_SECRET } from '../config'; import { BadRequestError, UnauthorizedRequestError } from '../utils/errors'; declare module 'jsonwebtoken' { @@ -27,7 +27,7 @@ const requireSignupAuth = async ( if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'})) const decodedToken = ( - jwt.verify(AUTH_TOKEN_VALUE, JWT_SIGNUP_SECRET) + jwt.verify(AUTH_TOKEN_VALUE, infisical.get('JWT_SIGNUP_SECRET')!) ); const user = await User.findOne({ diff --git a/backend/src/models/token.ts b/backend/src/models/token.ts index c003e42a2..e6f485f55 100644 --- a/backend/src/models/token.ts +++ b/backend/src/models/token.ts @@ -1,5 +1,4 @@ import { Schema, model } from 'mongoose'; -import { EMAIL_TOKEN_LIFETIME } from '../config'; export interface IToken { email: string; diff --git a/backend/src/services/PostHogClient.ts b/backend/src/services/PostHogClient.ts index 0d91a1c13..2d1bb8391 100644 --- a/backend/src/services/PostHogClient.ts +++ b/backend/src/services/PostHogClient.ts @@ -1,27 +1,41 @@ +import infisical from 'infisical-node'; import { PostHog } from 'posthog-node'; -import { - NODE_ENV, - POSTHOG_HOST, - POSTHOG_PROJECT_API_KEY, - TELEMETRY_ENABLED -} from '../config'; import { getLogger } from '../utils/logger'; -if(!TELEMETRY_ENABLED){ - getLogger("backend-main").info([ - "", - "To improve, Infisical collects telemetry data about general usage.", - "This helps us understand how the product is doing and guide our product development to create the best possible platform; it also helps us demonstrate growth as we support Infisical as open-source software.", - "To opt into telemetry, you can set `TELEMETRY_ENABLED=true` within the environment variables.", - ].join('\n')) +/** + * Logs telemetry enable/disable notice. + */ +const logTelemetryMessage = () => { + const TELEMETRY_ENABLED = infisical.get('TELEMETRY_ENABLED')! !== 'false' && true; + if(!TELEMETRY_ENABLED){ + getLogger("backend-main").info([ + "", + "To improve, Infisical collects telemetry data about general usage.", + "This helps us understand how the product is doing and guide our product development to create the best possible platform; it also helps us demonstrate growth as we support Infisical as open-source software.", + "To opt into telemetry, you can set `TELEMETRY_ENABLED=true` within the environment variables.", + ].join('\n')) + } } -let postHogClient: any; -if (NODE_ENV === 'production' && TELEMETRY_ENABLED) { - // case: enable opt-out telemetry in production - postHogClient = new PostHog(POSTHOG_PROJECT_API_KEY, { - host: POSTHOG_HOST - }); +/** + * Return an instance of the PostHog client initialized. + * @returns + */ +const getPostHogClient = () => { + let postHogClient: any; + const TELEMETRY_ENABLED = infisical.get('TELEMETRY_ENABLED')! !== 'false' && true; + if (infisical.get('NODE_ENV') === 'production' && TELEMETRY_ENABLED) { + // case: enable opt-out telemetry in production + postHogClient = new PostHog(infisical.get('POSTHOG_PROJECT_API_KEY')! || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE', { + host: infisical.get('POSTHOG_HOST')! + }); + } + + return postHogClient; +} + +export { + logTelemetryMessage, + getPostHogClient } -export default postHogClient; diff --git a/backend/src/services/index.ts b/backend/src/services/index.ts index 8ac393cf5..d98b70718 100644 --- a/backend/src/services/index.ts +++ b/backend/src/services/index.ts @@ -1,13 +1,14 @@ import DatabaseService from './DatabaseService'; -import postHogClient from './PostHogClient'; +import { logTelemetryMessage, getPostHogClient } from './PostHogClient'; import BotService from './BotService'; import EventService from './EventService'; import IntegrationService from './IntegrationService'; import TokenService from './TokenService'; export { + logTelemetryMessage, + getPostHogClient, DatabaseService, - postHogClient, BotService, EventService, IntegrationService, diff --git a/backend/src/services/smtp.ts b/backend/src/services/smtp.ts index 1bf809593..a9c056071 100644 --- a/backend/src/services/smtp.ts +++ b/backend/src/services/smtp.ts @@ -1,11 +1,5 @@ +import infisical from 'infisical-node'; import nodemailer from 'nodemailer'; -import { - SMTP_HOST, - SMTP_PORT, - SMTP_USERNAME, - SMTP_PASSWORD, - SMTP_SECURE -} from '../config'; import { SMTP_HOST_SENDGRID, SMTP_HOST_MAILGUN, @@ -15,54 +9,54 @@ import { import SMTPConnection from 'nodemailer/lib/smtp-connection'; import * as Sentry from '@sentry/node'; -const mailOpts: SMTPConnection.Options = { - host: SMTP_HOST, - port: SMTP_PORT as number -}; - -if (SMTP_USERNAME && SMTP_PASSWORD) { - mailOpts.auth = { - user: SMTP_USERNAME, - pass: SMTP_PASSWORD +export const initSmtp = () => { + const mailOpts: SMTPConnection.Options = { + host: infisical.get('SMTP_HOST')!, + port: parseInt(infisical.get('SMTP_PORT')!) }; -} -if (SMTP_SECURE) { - switch (SMTP_HOST) { - case SMTP_HOST_SENDGRID: - mailOpts.requireTLS = true; - break; - case SMTP_HOST_MAILGUN: - mailOpts.requireTLS = true; - mailOpts.tls = { - ciphers: 'TLSv1.2' - } - break; - case SMTP_HOST_SOCKETLABS: - mailOpts.requireTLS = true; - mailOpts.tls = { - ciphers: 'TLSv1.2' - } - break; - case SMTP_HOST_ZOHOMAIL: - mailOpts.requireTLS = true; - mailOpts.tls = { - ciphers: 'TLSv1.2' - } - break; - default: - if (SMTP_HOST.includes('amazonaws.com')) { + if (infisical.get('SMTP_USERNAME')! && infisical.get('SMTP_PASSWORD')!) { + mailOpts.auth = { + user: infisical.get('SMTP_USERNAME')!, + pass: infisical.get('SMTP_PASSWORD')! + }; + } + + if (infisical.get('SMTP_SECURE')! ? infisical.get('SMTP_SECURE')! === 'true' : false) { + switch (infisical.get('SMTP_HOST')!) { + case SMTP_HOST_SENDGRID: + mailOpts.requireTLS = true; + break; + case SMTP_HOST_MAILGUN: + mailOpts.requireTLS = true; mailOpts.tls = { ciphers: 'TLSv1.2' } - } else { - mailOpts.secure = true; - } - break; + break; + case SMTP_HOST_SOCKETLABS: + mailOpts.requireTLS = true; + mailOpts.tls = { + ciphers: 'TLSv1.2' + } + break; + case SMTP_HOST_ZOHOMAIL: + mailOpts.requireTLS = true; + mailOpts.tls = { + ciphers: 'TLSv1.2' + } + break; + default: + if (infisical.get('SMTP_HOST')!.includes('amazonaws.com')) { + mailOpts.tls = { + ciphers: 'TLSv1.2' + } + } else { + mailOpts.secure = true; + } + break; + } } -} -export const initSmtp = () => { const transporter = nodemailer.createTransport(mailOpts); transporter .verify() @@ -73,7 +67,7 @@ export const initSmtp = () => { .catch((err) => { Sentry.setUser(null); Sentry.captureException( - `SMTP - Failed to connect to ${SMTP_HOST}:${SMTP_PORT} \n\t${err}` + `SMTP - Failed to connect to ${infisical.get('SMTP_HOST')!}:${infisical.get('SMTP_PORT')!} \n\t${err}` ); }); diff --git a/backend/src/utils/addDevelopmentUser.ts b/backend/src/utils/addDevelopmentUser.ts index 2020cf3ad..ed886fd62 100644 --- a/backend/src/utils/addDevelopmentUser.ts +++ b/backend/src/utils/addDevelopmentUser.ts @@ -4,12 +4,12 @@ * ************************************************************************************************/ -import { NODE_ENV } from "../config" +import infisical from 'infisical-node'; import { Key, Membership, MembershipOrg, Organization, User, Workspace } from "../models"; import { Types } from 'mongoose'; export const createTestUserForDevelopment = async () => { - if (NODE_ENV === "development") { + if (infisical.get('NODE_ENV') === "development") { const testUserEmail = "test@localhost.local" const testUserPassword = "testInfisical1" const testUserId = "63cefa6ec8d3175601cfa980" diff --git a/backend/src/utils/logger.ts b/backend/src/utils/logger.ts index 64c65ea49..590a9d950 100644 --- a/backend/src/utils/logger.ts +++ b/backend/src/utils/logger.ts @@ -1,7 +1,7 @@ +import infisical from 'infisical-node'; /* eslint-disable no-console */ import { createLogger, format, transports } from 'winston'; import LokiTransport from 'winston-loki'; -import { LOKI_HOST, NODE_ENV } from '../config'; const { combine, colorize, label, printf, splat, timestamp } = format; @@ -25,10 +25,10 @@ const createLoggerWithLabel = (level: string, label: string) => { }) ] //* Add LokiTransport if it's enabled - if(LOKI_HOST !== undefined){ + if(infisical.get('LOKI_HOST')! !== undefined){ _transports.push( new LokiTransport({ - host: LOKI_HOST, + host: infisical.get('LOKI_HOST')!, handleExceptions: true, handleRejections: true, batching: true, @@ -37,7 +37,11 @@ const createLoggerWithLabel = (level: string, label: string) => { format: format.combine( format.json() ), - labels: {app: process.env.npm_package_name, version: process.env.npm_package_version, environment: NODE_ENV}, + labels: { + app: process.env.npm_package_name, + version: process.env.npm_package_version, + environment: infisical.get('NODE_ENV')! + }, onConnectionError: (err: Error)=> console.error('Connection error while connecting to Loki Server.\n', err) }) ) diff --git a/backend/src/utils/requestError.ts b/backend/src/utils/requestError.ts index da2803da7..7e7fd6070 100644 --- a/backend/src/utils/requestError.ts +++ b/backend/src/utils/requestError.ts @@ -1,5 +1,5 @@ +import infisical from 'infisical-node'; import { Request } from 'express' -import { VERBOSE_ERROR_OUTPUT } from '../config' export enum LogLevel { DEBUG = 100, @@ -87,6 +87,7 @@ export default class RequestError extends Error{ }, this.context) //* Omit sensitive information from context that can leak internal workings of this program if user is not developer + const VERBOSE_ERROR_OUTPUT = infisical.get('VERBOSE_ERROR_OUTPUT')! === 'true' && true; if(!VERBOSE_ERROR_OUTPUT){ _context = this._omit(_context, [ 'stacktrace', diff --git a/backend/src/variables/index.ts b/backend/src/variables/index.ts index f7c86e0f8..b71044cba 100644 --- a/backend/src/variables/index.ts +++ b/backend/src/variables/index.ts @@ -34,7 +34,7 @@ import { INTEGRATION_FLYIO_API_URL, INTEGRATION_CIRCLECI_API_URL, INTEGRATION_TRAVISCI_API_URL, - INTEGRATION_OPTIONS, + getIntegrationOptions } from "./integration"; import { OWNER, ADMIN, MEMBER, INVITED, ACCEPTED } from "./organization"; import { SECRET_SHARED, SECRET_PERSONAL } from "./secret"; @@ -113,7 +113,7 @@ export { ACTION_UPDATE_SECRETS, ACTION_DELETE_SECRETS, ACTION_READ_SECRETS, - INTEGRATION_OPTIONS, + getIntegrationOptions, SMTP_HOST_SENDGRID, SMTP_HOST_MAILGUN, SMTP_HOST_SOCKETLABS, diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index 5ed93a597..db8adf105 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -1,13 +1,4 @@ -import { - CLIENT_ID_AZURE, - CLIENT_ID_GITLAB -} from '../config'; -import { - CLIENT_ID_HEROKU, - CLIENT_ID_NETLIFY, - CLIENT_ID_GITHUB, - CLIENT_SLUG_VERCEL -} from "../config"; +import infisical from 'infisical-node'; // integrations const INTEGRATION_AZURE_KEY_VAULT = 'azure-key-vault'; @@ -48,7 +39,6 @@ const INTEGRATION_GITHUB_TOKEN_URL = "https://github.com/login/oauth/access_token"; const INTEGRATION_GITLAB_TOKEN_URL = "https://gitlab.com/oauth/token"; - // integration apps endpoints const INTEGRATION_HEROKU_API_URL = "https://api.heroku.com"; const INTEGRATION_GITLAB_API_URL = "https://gitlab.com/api"; @@ -59,156 +49,160 @@ const INTEGRATION_FLYIO_API_URL = "https://api.fly.io/graphql"; const INTEGRATION_CIRCLECI_API_URL = "https://circleci.com/api"; const INTEGRATION_TRAVISCI_API_URL = "https://api.travis-ci.com"; -// TODO: deprecate types? -const INTEGRATION_OPTIONS = [ - { - name: 'Heroku', - slug: 'heroku', - image: 'Heroku.png', - isAvailable: true, - type: 'oauth', - clientId: CLIENT_ID_HEROKU, - docsLink: '' - }, - { - name: 'Vercel', - slug: 'vercel', - image: 'Vercel.png', - isAvailable: true, - type: 'oauth', - clientId: '', - clientSlug: CLIENT_SLUG_VERCEL, - docsLink: '' - }, - { - name: 'Netlify', - slug: 'netlify', - image: 'Netlify.png', - isAvailable: true, - type: 'oauth', - clientId: CLIENT_ID_NETLIFY, - docsLink: '' - }, - { - name: 'GitHub', - slug: 'github', - image: 'GitHub.png', - isAvailable: true, - type: 'oauth', - clientId: CLIENT_ID_GITHUB, - docsLink: '' - }, - { - name: 'Render', - slug: 'render', - image: 'Render.png', - isAvailable: true, - type: 'pat', - clientId: '', - docsLink: '' - }, - { - name: 'Fly.io', - slug: 'flyio', - image: 'Flyio.svg', - isAvailable: true, - type: 'pat', - clientId: '', - docsLink: '' - }, - { - name: 'AWS Parameter Store', - slug: 'aws-parameter-store', - image: 'Amazon Web Services.png', - isAvailable: true, - type: 'custom', - clientId: '', - docsLink: '' - }, - { - name: 'AWS Secret Manager', - slug: 'aws-secret-manager', - image: 'Amazon Web Services.png', - isAvailable: true, - type: 'custom', - clientId: '', - docsLink: '' - }, - { - name: 'Azure Key Vault', - slug: 'azure-key-vault', - image: 'Microsoft Azure.png', - isAvailable: true, - type: 'oauth', - clientId: CLIENT_ID_AZURE, - docsLink: '' - }, - { - name: 'Circle CI', - slug: 'circleci', - image: 'Circle CI.png', - isAvailable: true, - type: 'pat', - clientId: '', - docsLink: '' - }, - { - name: 'GitLab', - slug: 'gitlab', - image: 'GitLab.png', - isAvailable: true, - type: 'custom', - clientId: CLIENT_ID_GITLAB, - docsLink: '' - }, - { - name: 'Travis CI', - slug: 'travisci', - image: 'Travis CI.png', - isAvailable: true, - type: 'pat', - clientId: '', - docsLink: '' - }, - { - name: 'Google Cloud Platform', - slug: 'gcp', - image: 'Google Cloud Platform.png', - isAvailable: false, - type: '', - clientId: '', - docsLink: '' - } -] +const getIntegrationOptions = () => { + const INTEGRATION_OPTIONS = [ + { + name: 'Heroku', + slug: 'heroku', + image: 'Heroku.png', + isAvailable: true, + type: 'oauth', + clientId: infisical.get('CLIENT_ID_HEROKU')!, + docsLink: '' + }, + { + name: 'Vercel', + slug: 'vercel', + image: 'Vercel.png', + isAvailable: true, + type: 'oauth', + clientId: '', + clientSlug: infisical.get('CLIENT_SLUG_VERCEL')!, + docsLink: '' + }, + { + name: 'Netlify', + slug: 'netlify', + image: 'Netlify.png', + isAvailable: true, + type: 'oauth', + clientId: infisical.get('CLIENT_ID_NETLIFY')!, + docsLink: '' + }, + { + name: 'GitHub', + slug: 'github', + image: 'GitHub.png', + isAvailable: true, + type: 'oauth', + clientId: infisical.get('CLIENT_ID_GITHUB')!, + docsLink: '' + }, + { + name: 'Render', + slug: 'render', + image: 'Render.png', + isAvailable: true, + type: 'pat', + clientId: '', + docsLink: '' + }, + { + name: 'Fly.io', + slug: 'flyio', + image: 'Flyio.svg', + isAvailable: true, + type: 'pat', + clientId: '', + docsLink: '' + }, + { + name: 'AWS Parameter Store', + slug: 'aws-parameter-store', + image: 'Amazon Web Services.png', + isAvailable: true, + type: 'custom', + clientId: '', + docsLink: '' + }, + { + name: 'AWS Secret Manager', + slug: 'aws-secret-manager', + image: 'Amazon Web Services.png', + isAvailable: true, + type: 'custom', + clientId: '', + docsLink: '' + }, + { + name: 'Azure Key Vault', + slug: 'azure-key-vault', + image: 'Microsoft Azure.png', + isAvailable: true, + type: 'oauth', + clientId: infisical.get('CLIENT_ID_AZURE')!, + docsLink: '' + }, + { + name: 'Circle CI', + slug: 'circleci', + image: 'Circle CI.png', + isAvailable: true, + type: 'pat', + clientId: '', + docsLink: '' + }, + { + name: 'GitLab', + slug: 'gitlab', + image: 'GitLab.png', + isAvailable: true, + type: 'custom', + clientId: infisical.get('CLIENT_ID_GITLAB'), + docsLink: '' + }, + { + name: 'Travis CI', + slug: 'travisci', + image: 'Travis CI.png', + isAvailable: true, + type: 'pat', + clientId: '', + docsLink: '' + }, + { + name: 'Google Cloud Platform', + slug: 'gcp', + image: 'Google Cloud Platform.png', + isAvailable: false, + type: '', + clientId: '', + docsLink: '' + } + ] + + return INTEGRATION_OPTIONS; +} + export { INTEGRATION_AZURE_KEY_VAULT, INTEGRATION_AWS_PARAMETER_STORE, INTEGRATION_AWS_SECRET_MANAGER, - INTEGRATION_HEROKU, - INTEGRATION_VERCEL, - INTEGRATION_NETLIFY, - INTEGRATION_GITHUB, - INTEGRATION_GITLAB, - INTEGRATION_RENDER, - INTEGRATION_FLYIO, - INTEGRATION_CIRCLECI, - INTEGRATION_TRAVISCI, - INTEGRATION_SET, - INTEGRATION_OAUTH2, + INTEGRATION_HEROKU, + INTEGRATION_VERCEL, + INTEGRATION_NETLIFY, + INTEGRATION_GITHUB, + INTEGRATION_GITLAB, + INTEGRATION_RENDER, + INTEGRATION_FLYIO, + INTEGRATION_CIRCLECI, + INTEGRATION_TRAVISCI, + INTEGRATION_SET, + INTEGRATION_OAUTH2, INTEGRATION_AZURE_TOKEN_URL, - INTEGRATION_HEROKU_TOKEN_URL, - INTEGRATION_VERCEL_TOKEN_URL, - INTEGRATION_NETLIFY_TOKEN_URL, - INTEGRATION_GITHUB_TOKEN_URL, - INTEGRATION_GITLAB_API_URL, - INTEGRATION_HEROKU_API_URL, - INTEGRATION_GITLAB_TOKEN_URL, - INTEGRATION_VERCEL_API_URL, - INTEGRATION_NETLIFY_API_URL, - INTEGRATION_RENDER_API_URL, - INTEGRATION_FLYIO_API_URL, - INTEGRATION_CIRCLECI_API_URL, - INTEGRATION_TRAVISCI_API_URL, - INTEGRATION_OPTIONS, + INTEGRATION_HEROKU_TOKEN_URL, + INTEGRATION_VERCEL_TOKEN_URL, + INTEGRATION_NETLIFY_TOKEN_URL, + INTEGRATION_GITHUB_TOKEN_URL, + INTEGRATION_GITLAB_API_URL, + INTEGRATION_HEROKU_API_URL, + INTEGRATION_GITLAB_TOKEN_URL, + INTEGRATION_VERCEL_API_URL, + INTEGRATION_NETLIFY_API_URL, + INTEGRATION_RENDER_API_URL, + INTEGRATION_FLYIO_API_URL, + INTEGRATION_CIRCLECI_API_URL, + INTEGRATION_TRAVISCI_API_URL, + getIntegrationOptions }; diff --git a/docs/self-hosting/configuration/envars.mdx b/docs/self-hosting/configuration/envars.mdx index 342fa9e96..f674c1bcd 100644 --- a/docs/self-hosting/configuration/envars.mdx +++ b/docs/self-hosting/configuration/envars.mdx @@ -17,7 +17,6 @@ Configuring Infisical requires setting some environment variables. There is a fi | `JWT_REFRESH_LIFETIME` | JWT token lifetime expressed in seconds or a string describing a time span (e.g. 60, "2 days", "10h", "7d") | `90d` | | `JWT_AUTH_LIFETIME` | JWT token lifetime expressed in seconds or a string describing a time span (e.g. 60, "2 days", "10h", "7d") | `10d` | | `JWT_MFA_LIFETIME` | JWT token lifetime expressed in seconds or a string describing a time span (e.g. 60, "2 days", "10h", "7d") | `5m` | -| `EMAIL_TOKEN_LIFETIME` | Email OTP/magic-link lifetime expressed in seconds | `86400` | | `MONGO_URL` | ❗️ MongoDB instance connection string either to container instance or MongoDB Cloud | `None` | | `MONGO_USERNAME` | MongoDB username if using container | `None` | | `MONGO_PASSWORD` | MongoDB password if using container | `None` |