From 7fd06e36bca485d37db494d899bbee13e16ea2d2 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Tue, 14 Mar 2023 16:38:30 +0700 Subject: [PATCH 1/7] Complete preliminary addition of infisical-node to support service tokens --- .env.example | 3 - backend/environment.d.ts | 1 - backend/package-lock.json | 21 ++ backend/package.json | 1 + backend/src/app.ts | 144 -------- backend/src/config/index.ts | 200 ++++++------ backend/src/controllers/v1/authController.ts | 21 +- .../v1/integrationAuthController.ts | 10 +- .../controllers/v1/membershipController.ts | 8 +- .../controllers/v1/membershipOrgController.ts | 8 +- .../controllers/v1/organizationController.ts | 28 +- .../src/controllers/v1/passwordController.ts | 8 +- .../src/controllers/v1/secretController.ts | 5 +- .../controllers/v1/serviceTokenController.ts | 4 +- .../src/controllers/v1/signupController.ts | 8 +- .../src/controllers/v1/stripeController.ts | 11 +- .../controllers/v2/apiKeyDataController.ts | 8 +- backend/src/controllers/v2/authController.ts | 16 +- .../src/controllers/v2/secretController.ts | 9 +- .../src/controllers/v2/secretsController.ts | 9 +- .../v2/serviceTokenDataController.ts | 8 +- .../src/controllers/v2/signupController.ts | 6 +- .../src/controllers/v2/workspaceController.ts | 4 +- .../src/ee/controllers/v1/stripeController.ts | 13 +- backend/src/ee/services/EELicenseService.ts | 4 +- backend/src/helpers/auth.ts | 19 +- backend/src/helpers/bot.ts | 6 +- backend/src/helpers/nodemailer.ts | 6 +- backend/src/helpers/organization.ts | 35 +- backend/src/helpers/token.ts | 6 +- backend/src/index.ts | 168 +++++++++- backend/src/integrations/exchange.ts | 49 +-- backend/src/integrations/refresh.ts | 30 +- backend/src/middleware/requestErrorHandler.ts | 9 +- backend/src/middleware/requireMfaAuth.ts | 4 +- .../src/middleware/requireServiceTokenAuth.ts | 4 +- backend/src/middleware/requireSignupAuth.ts | 4 +- backend/src/models/token.ts | 1 - backend/src/services/PostHogClient.ts | 54 +-- backend/src/services/index.ts | 5 +- backend/src/services/smtp.ts | 92 +++--- backend/src/utils/addDevelopmentUser.ts | 4 +- backend/src/utils/logger.ts | 12 +- backend/src/utils/requestError.ts | 3 +- backend/src/variables/index.ts | 4 +- backend/src/variables/integration.ts | 308 +++++++++--------- docs/self-hosting/configuration/envars.mdx | 1 - 47 files changed, 691 insertions(+), 691 deletions(-) delete mode 100644 backend/src/app.ts 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` | From a6c8638345ea84f519954225b9ea7815ba9e660c Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Wed, 15 Mar 2023 00:09:40 +0700 Subject: [PATCH 2/7] Refactor infisical-node to config file for birds eye view of envars --- backend/src/config/index.ts | 150 ++++++------------ backend/src/controllers/v1/authController.ts | 17 +- .../controllers/v1/membershipController.ts | 4 +- .../controllers/v1/membershipOrgController.ts | 8 +- .../controllers/v1/organizationController.ts | 12 +- .../src/controllers/v1/passwordController.ts | 8 +- .../controllers/v1/serviceTokenController.ts | 4 +- .../src/controllers/v1/signupController.ts | 8 +- .../src/controllers/v1/stripeController.ts | 6 +- .../controllers/v2/apiKeyDataController.ts | 4 +- backend/src/controllers/v2/authController.ts | 17 +- .../v2/serviceTokenDataController.ts | 6 +- .../src/controllers/v2/signupController.ts | 6 +- .../src/ee/controllers/v1/stripeController.ts | 6 +- backend/src/helpers/auth.ts | 17 +- backend/src/helpers/bot.ts | 6 +- backend/src/helpers/nodemailer.ts | 4 +- backend/src/helpers/organization.ts | 23 +-- backend/src/helpers/token.ts | 4 +- backend/src/index.ts | 27 ++-- backend/src/integrations/exchange.ts | 47 ++++-- backend/src/integrations/refresh.ts | 28 ++-- backend/src/middleware/requestErrorHandler.ts | 6 +- backend/src/middleware/requireMfaAuth.ts | 4 +- .../src/middleware/requireServiceTokenAuth.ts | 4 +- backend/src/middleware/requireSignupAuth.ts | 4 +- backend/src/services/PostHogClient.ts | 17 +- backend/src/services/smtp.ts | 26 +-- backend/src/utils/addDevelopmentUser.ts | 4 +- backend/src/utils/logger.ts | 8 +- backend/src/utils/requestError.ts | 5 +- backend/src/variables/integration.ts | 21 ++- docs/sdk/overview/usage.mdx | 15 +- 33 files changed, 264 insertions(+), 262 deletions(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 06ad3a631..c4e072259 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,101 +1,49 @@ -// 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, - // 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, -}; +import infisical from 'infisical-node'; +export const getPort = () => infisical.get('PORT')! || 4000; +export const getInviteOnlySignup = () => infisical.get('INVITE_ONLY_SIGNUP')! == undefined ? false : process.env.INVITE_ONLY_SIGNUP; +export const getEncryptionKey = () => infisical.get('ENCRYPTION_KEY')!; +export const getSaltRounds = () => parseInt(infisical.get('SALT_ROUNDS')!) || 10; +export const getJwtAuthLifetime = () => infisical.get('JWT_AUTH_LIFETIME')! || '10d'; +export const getJwtAuthSecret = () => infisical.get('JWT_AUTH_SECRET')!; +export const getJwtMfaLifetime = () => infisical.get('JWT_MFA_LIFETIME')!; +export const getJwtMfaSecret = () => infisical.get('JWT_MFA_LIFETIME')! || '5m'; +export const getJwtRefreshLifetime = () => infisical.get('JWT_REFRESH_LIFETIME')! || '90d'; +export const getJwtRefreshSecret = () => infisical.get('JWT_REFRESH_SECRET')!; +export const getJwtServiceSecret = () => infisical.get('JWT_SERVICE_SECRET')!; +export const getJwtSignupLifetime = () => infisical.get('JWT_SIGNUP_LIFETIME')!; +export const getJwtSignupSecret = () => infisical.get('JWT_SIGNUP_SECRET')!; +export const getMongoURL = () => infisical.get('MONGO_URL')!; +export const getNodeEnv = () => infisical.get('NODE_ENV')!; +export const getVerboseErrorOutput = () => infisical.get('VERBOSE_ERROR_OUTPUT')! === 'true' && true; +export const getLokiHost = () => infisical.get('LOKI_HOST')!; +export const getClientIdAzure = () => infisical.get('CLIENT_ID_AZURE')!; +export const getClientIdHeroku = () => infisical.get('CLIENT_ID_HEROKU')!; +export const getClientIdVercel = () => infisical.get('CLIENT_ID_VERCEL')!; +export const getClientIdNetlify = () => infisical.get('CLIENT_ID_NETLIFY')!; +export const getClientIdGitHub = () => infisical.get('CLIENT_ID_GITHUB')!; +export const getClientIdGitLab = () => infisical.get('CLIENT_ID_GITLAB')!; +export const getClientSecretAzure = () => infisical.get('CLIENT_SECRET_AZURE')!; +export const getClientSecretHeroku = () => infisical.get('CLIENT_SECRET_HEROKU')!; +export const getClientSecretVercel = () => infisical.get('CLIENT_SECRET_VERCEL')!; +export const getClientSecretNetlify = () => infisical.get('CLIENT_SECRET_NETLIFY')!; +export const getClientSecretGitHub = () => infisical.get('CLIENT_SECRET_GITHUB')!; +export const getClientSecretGitLab = () => infisical.get('CLIENT_SECRET_GITLAB')!; +export const getClientSlugVercel = () => infisical.get('CLIENT_SLUG_VERCEL')!; +export const getPostHogHost = () => infisical.get('POSTHOG_HOST')! || 'https://app.posthog.com'; +export const getPostHogProjectApiKey = () => infisical.get('POSTHOG_PROJECT_API_KEY')! || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE'; +export const getSentryDSN = () => infisical.get('SENTRY_DSN')!; +export const getSiteURL = () => infisical.get('SITE_URL')!; +export const getSmtpHost = () => infisical.get('SMTP_HOST')!; +export const getSmtpSecure = () => infisical.get('SMTP_SECURE')! === 'true' || false; +export const getSmtpPort = () => parseInt(infisical.get('SMTP_PORT')!) || 587; +export const getSmtpUsername = () => infisical.get('SMTP_USERNAME')!; +export const getSmtpPassword = () => infisical.get('SMTP_PASSWORD')!; +export const getSmtpFromAddress = () => infisical.get('SMTP_FROM_ADDRESS')!; +export const getSmtpFromName = () => infisical.get('SMTP_FROM_NAME')! || 'Infisical'; +export const getStripeProductStarter = () => infisical.get('STRIPE_PRODUCT_STARTER')!; +export const getStripeProductPro = () => infisical.get('STRIPE_PRODUCT_PRO')!; +export const getStripeProductTeam = () => infisical.get('STRIPE_PRODUCT_TEAM')!; +export const getStripePublishableKey = () => infisical.get('STRIPE_PUBLISHABLE_KEY')!; +export const getStripeSecretKey = () => infisical.get('STRIPE_SECRET_KEY')!; +export const getStripeWebhookSecret = () => infisical.get('STRIPE_WEBHOOK_SECRET')!; +export const getTelemetryEnabled = () => infisical.get('TELEMETRY_ENABLED')! !== 'false' && true; \ No newline at end of file diff --git a/backend/src/controllers/v1/authController.ts b/backend/src/controllers/v1/authController.ts index 732715967..3c6ec523a 100644 --- a/backend/src/controllers/v1/authController.ts +++ b/backend/src/controllers/v1/authController.ts @@ -1,5 +1,4 @@ import * as Sentry from '@sentry/node'; -import infisical from 'infisical-node'; import { Request, Response } from 'express'; import jwt from 'jsonwebtoken'; import * as bigintConversion from 'bigint-conversion'; @@ -15,6 +14,12 @@ import { import { BadRequestError } from '../../utils/errors'; import { EELogService } from '../../ee/services'; import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this +import { + getNodeEnv, + getJwtRefreshSecret, + getJwtAuthLifetime, + getJwtAuthSecret +} from '../../config'; declare module 'jsonwebtoken' { export interface UserIDJwtPayload extends jwt.JwtPayload { @@ -121,7 +126,7 @@ export const login2 = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: infisical.get('NODE_ENV')! === 'production' ? true : false + secure: getNodeEnv() === 'production' ? true : false }); const loginAction = await EELogService.createAction({ @@ -177,7 +182,7 @@ export const logout = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: infisical.get('NODE_ENV') === 'production' ? true : false + secure: getNodeEnv() === 'production' ? true : false }); const logoutAction = await EELogService.createAction({ @@ -232,7 +237,7 @@ export const getNewToken = async (req: Request, res: Response) => { } const decodedToken = ( - jwt.verify(refreshToken, infisical.get('JWT_REFRESH_SECRET')!) + jwt.verify(refreshToken, getJwtRefreshSecret()) ); const user = await User.findOne({ @@ -247,8 +252,8 @@ export const getNewToken = async (req: Request, res: Response) => { payload: { userId: decodedToken.userId }, - expiresIn: infisical.get('JWT_AUTH_LIFETIME')!, - secret: infisical.get('JWT_AUTH_SECRET')! + expiresIn: getJwtAuthLifetime(), + secret: getJwtAuthSecret() }); return res.status(200).send({ diff --git a/backend/src/controllers/v1/membershipController.ts b/backend/src/controllers/v1/membershipController.ts index 11fc465c2..436be9dc4 100644 --- a/backend/src/controllers/v1/membershipController.ts +++ b/backend/src/controllers/v1/membershipController.ts @@ -1,4 +1,3 @@ -import infisical from 'infisical-node'; import * as Sentry from '@sentry/node'; import { Request, Response } from 'express'; import { Membership, MembershipOrg, User, Key } from '../../models'; @@ -8,6 +7,7 @@ import { } from '../../helpers/membership'; import { sendMail } from '../../helpers/nodemailer'; import { ADMIN, MEMBER, ACCEPTED } from '../../variables'; +import { getSiteURL } from '../../config'; /** * Check that user is a member of workspace with id [workspaceId] @@ -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: infisical.get('SITE_URL')! + '/login' + callback_url: getSiteURL() + '/login' } }); } catch (err) { diff --git a/backend/src/controllers/v1/membershipOrgController.ts b/backend/src/controllers/v1/membershipOrgController.ts index 0efe00dac..105be0503 100644 --- a/backend/src/controllers/v1/membershipOrgController.ts +++ b/backend/src/controllers/v1/membershipOrgController.ts @@ -1,4 +1,3 @@ -import infisical from 'infisical-node'; import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; import { MembershipOrg, Organization, User } from '../../models'; @@ -8,6 +7,7 @@ import { updateSubscriptionOrgQuantity } from '../../helpers/organization'; import { sendMail } from '../../helpers/nodemailer'; import { TokenService } from '../../services'; import { OWNER, ADMIN, MEMBER, ACCEPTED, INVITED, TOKEN_EMAIL_ORG_INVITATION } from '../../variables'; +import { getSiteURL, getJwtSignupLifetime, getJwtSignupSecret } from '../../config'; /** * Delete organization membership with id [membershipOrgId] from organization @@ -178,7 +178,7 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => { organizationName: organization.name, email: inviteeEmail, token, - callback_url: infisical.get('SITE_URL') + '/signupinvite' + callback_url: getSiteURL() + '/signupinvite' } }); } @@ -250,8 +250,8 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => { payload: { userId: user._id.toString() }, - expiresIn: infisical.get('JWT_SIGNUP_LIFETIME')!, - secret: infisical.get('JWT_SIGNUP_SECRET')! + expiresIn: getJwtSignupLifetime(), + secret: getJwtSignupSecret() }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/controllers/v1/organizationController.ts b/backend/src/controllers/v1/organizationController.ts index 44403db86..00ad87b82 100644 --- a/backend/src/controllers/v1/organizationController.ts +++ b/backend/src/controllers/v1/organizationController.ts @@ -1,4 +1,3 @@ -import infisical from 'infisical-node'; import * as Sentry from '@sentry/node'; import { Request, Response } from 'express'; import Stripe from 'stripe'; @@ -13,6 +12,7 @@ import { createOrganization as create } from '../../helpers/organization'; import { addMembershipsOrg } from '../../helpers/membershipOrg'; import { OWNER, ACCEPTED } from '../../variables'; import _ from 'lodash'; +import { getStripeSecretKey, getSiteURL } from '../../config'; export const getOrganizations = async (req: Request, res: Response) => { let organizations; @@ -317,7 +317,7 @@ export const createOrganizationPortalSession = async ( ) => { let session; try { - const stripe = new Stripe(infisical.get('STRIPE_SECRET_KEY')!, { + const stripe = new Stripe(getStripeSecretKey(), { apiVersion: '2022-08-01' }); @@ -333,13 +333,13 @@ export const createOrganizationPortalSession = async ( customer: req.membershipOrg.organization.customerId, mode: 'setup', payment_method_types: ['card'], - success_url: infisical.get('SITE_URL')! + '/dashboard', - cancel_url: infisical.get('SITE_URL')! + '/dashboard' + success_url: getSiteURL() + '/dashboard', + cancel_url: getSiteURL() + '/dashboard' }); } else { session = await stripe.billingPortal.sessions.create({ customer: req.membershipOrg.organization.customerId, - return_url: infisical.get('SITE_URL') + '/dashboard' + return_url: getSiteURL() + '/dashboard' }); } @@ -365,7 +365,7 @@ export const getOrganizationSubscriptions = async ( ) => { let subscriptions; try { - const stripe = new Stripe(infisical.get('STRIPE_SECRET_KEY')!, { + const stripe = new Stripe(getStripeSecretKey(), { apiVersion: '2022-08-01' }); diff --git a/backend/src/controllers/v1/passwordController.ts b/backend/src/controllers/v1/passwordController.ts index 72a649bb1..fed24419c 100644 --- a/backend/src/controllers/v1/passwordController.ts +++ b/backend/src/controllers/v1/passwordController.ts @@ -1,4 +1,3 @@ -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 @@ -10,6 +9,7 @@ import { sendMail } from '../../helpers/nodemailer'; import { TokenService } from '../../services'; import { TOKEN_EMAIL_PASSWORD_RESET } from '../../variables'; import { BadRequestError } from '../../utils/errors'; +import { getSiteURL, getJwtSignupLifetime, getJwtSignupSecret } from '../../config'; /** * Password reset step 1: Send email verification link to email [email] @@ -44,7 +44,7 @@ export const emailPasswordReset = async (req: Request, res: Response) => { substitutions: { email, token, - callback_url: infisical.get('SITE_URL')! + '/password-reset' + callback_url: getSiteURL() + '/password-reset' } }); } catch (err) { @@ -91,8 +91,8 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => { payload: { userId: user._id.toString() }, - expiresIn: infisical.get('JWT_SIGNUP_LIFETIME')!, - secret: infisical.get('JWT_SIGNUP_SECRET')! + expiresIn: getJwtSignupLifetime(), + secret: getJwtSignupSecret() }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/controllers/v1/serviceTokenController.ts b/backend/src/controllers/v1/serviceTokenController.ts index 76899a1a7..9f241349a 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 { getJwtServiceSecret } from '../../config'; /** * Return service token on request @@ -61,7 +61,7 @@ export const createServiceToken = async (req: Request, res: Response) => { workspaceId }, expiresIn: expiresIn, - secret: infisical.get('JWT_SERVICE_SECRET')! + secret: getJwtServiceSecret() }); } catch (err) { return res.status(400).send({ diff --git a/backend/src/controllers/v1/signupController.ts b/backend/src/controllers/v1/signupController.ts index dbf5bd5a4..cb411a29e 100644 --- a/backend/src/controllers/v1/signupController.ts +++ b/backend/src/controllers/v1/signupController.ts @@ -1,4 +1,3 @@ -import infisical from 'infisical-node'; import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; import { User } from '../../models'; @@ -8,6 +7,7 @@ import { } from '../../helpers/signup'; import { createToken } from '../../helpers/auth'; import { BadRequestError } from '../../utils/errors'; +import { getInviteOnlySignup, getJwtSignupLifetime, getJwtSignupSecret } from '../../config'; /** * Signup step 1: Initialize account for user under email [email] and send a verification code @@ -21,7 +21,7 @@ export const beginEmailSignup = async (req: Request, res: Response) => { try { email = req.body.email; - if (infisical.get('INVITE_ONLY_SIGNUP') || false) { + if (getInviteOnlySignup() || 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: infisical.get('JWT_SIGNUP_LIFETIME')!, - secret: infisical.get('JWT_SIGNUP_SECRET')! + expiresIn: getJwtSignupLifetime(), + secret: getJwtSignupSecret() }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/controllers/v1/stripeController.ts b/backend/src/controllers/v1/stripeController.ts index 1d3ee0bf2..1a981c088 100644 --- a/backend/src/controllers/v1/stripeController.ts +++ b/backend/src/controllers/v1/stripeController.ts @@ -1,7 +1,7 @@ -import infisical from 'infisical-node'; import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; import Stripe from 'stripe'; +import { getStripeSecretKey, getStripeWebhookSecret } from '../../config'; /** * Handle service provisioning/un-provisioning via Stripe @@ -13,7 +13,7 @@ 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')!, { + const stripe = new Stripe(getStripeSecretKey(), { apiVersion: '2022-08-01' }); @@ -21,7 +21,7 @@ export const handleWebhook = async (req: Request, res: Response) => { event = stripe.webhooks.constructEvent( req.body, sig, - infisical.get('STRIPE_WEBHOOK_SECRET')! + getStripeWebhookSecret() ); } 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 b6819928a..fd87f7306 100644 --- a/backend/src/controllers/v2/apiKeyDataController.ts +++ b/backend/src/controllers/v2/apiKeyDataController.ts @@ -1,11 +1,11 @@ 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 { getSaltRounds } from '../../config'; /** * Return API key data for user with id [req.user_id] @@ -43,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, parseInt(infisical.get('SALT_ROUNDS')!) || 10); + const secretHash = await bcrypt.hash(secret, getSaltRounds()); 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 4401e2498..b0204e1b9 100644 --- a/backend/src/controllers/v2/authController.ts +++ b/backend/src/controllers/v2/authController.ts @@ -1,4 +1,3 @@ -import infisical from 'infisical-node'; /* eslint-disable @typescript-eslint/no-var-requires */ import { Request, Response } from 'express'; import jwt from 'jsonwebtoken'; @@ -17,6 +16,11 @@ import { ACTION_LOGIN } from '../../variables'; import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this +import { + getNodeEnv, + getJwtMfaLifetime, + getJwtMfaSecret +} from '../../config'; declare module 'jsonwebtoken' { export interface UserIDJwtPayload extends jwt.JwtPayload { @@ -120,8 +124,8 @@ export const login2 = async (req: Request, res: Response) => { payload: { userId: user._id.toString() }, - expiresIn: infisical.get('JWT_MFA_LIFETIME')!, - secret: infisical.get('JWT_MFA_SECRET')! + expiresIn: getJwtMfaLifetime(), + secret: getJwtMfaSecret() }); const code = await TokenService.createToken({ @@ -159,7 +163,7 @@ export const login2 = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: infisical.get('NODE_ENV')! === 'production' ? true : false + secure: getNodeEnv() === 'production' ? true : false }); // case: user does not have MFA enablgged @@ -298,7 +302,7 @@ export const verifyMfaToken = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: infisical.get('NODE_ENV')! === 'production' ? true : false + secure: getNodeEnv() === 'production' ? true : false }); interface VerifyMfaTokenRes { @@ -341,5 +345,4 @@ export const verifyMfaToken = async (req: Request, res: Response) => { }); return res.status(200).send(resObj); -} - +} \ No newline at end of file diff --git a/backend/src/controllers/v2/serviceTokenDataController.ts b/backend/src/controllers/v2/serviceTokenDataController.ts index 295664cf9..a4e06f8e4 100644 --- a/backend/src/controllers/v2/serviceTokenDataController.ts +++ b/backend/src/controllers/v2/serviceTokenDataController.ts @@ -1,5 +1,4 @@ import * as Sentry from '@sentry/node'; -import infisical from 'infisical-node'; import { Request, Response } from 'express'; import crypto from 'crypto'; import bcrypt from 'bcrypt'; @@ -8,6 +7,7 @@ import { } from '../../models'; import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissions'; import { ABILITY_READ } from '../../variables/organization'; +import { getSaltRounds } from '../../config'; /** * Return service token data associated with service token on request @@ -73,7 +73,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => { } const secret = crypto.randomBytes(16).toString('hex'); - const secretHash = await bcrypt.hash(secret, parseInt(infisical.get('SALT_ROUNDS')!) || 10); + const secretHash = await bcrypt.hash(secret, getSaltRounds()); const expiresAt = new Date(); expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn); @@ -140,4 +140,4 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => { function UnauthorizedRequestError(arg0: { message: string; }) { throw new Error('Function not implemented.'); -} +} \ No newline at end of file diff --git a/backend/src/controllers/v2/signupController.ts b/backend/src/controllers/v2/signupController.ts index 0eb148e37..3e4b1f44a 100644 --- a/backend/src/controllers/v2/signupController.ts +++ b/backend/src/controllers/v2/signupController.ts @@ -1,4 +1,3 @@ -import infisical from 'infisical-node'; import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; import { User, MembershipOrg } from '../../models'; @@ -9,6 +8,7 @@ import { import { issueAuthTokens } from '../../helpers/auth'; import { INVITED, ACCEPTED } from '../../variables'; import request from '../../config/request'; +import { getNodeEnv } from '../../config'; /** * Complete setting up user by adding their personal and auth information as part of the @@ -127,7 +127,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: infisical.get('NODE_ENV')! === 'production' ? true : false + secure: getNodeEnv() === '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: infisical.get('NODE_ENV')! === 'production' ? true : false + secure: getNodeEnv() === 'production' ? true : false }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/ee/controllers/v1/stripeController.ts b/backend/src/ee/controllers/v1/stripeController.ts index 7c816550e..3caa0f395 100644 --- a/backend/src/ee/controllers/v1/stripeController.ts +++ b/backend/src/ee/controllers/v1/stripeController.ts @@ -1,7 +1,7 @@ import * as Sentry from '@sentry/node'; -import infisical from 'infisical-node'; import { Request, Response } from 'express'; import Stripe from 'stripe'; +import { getStripeSecretKey, getStripeWebhookSecret } from '../../../config'; /** * Handle service provisioning/un-provisioning via Stripe @@ -12,7 +12,7 @@ import Stripe from 'stripe'; export const handleWebhook = async (req: Request, res: Response) => { let event; try { - const stripe = new Stripe(infisical.get('STRIPE_SECRET_KEY')!, { + const stripe = new Stripe(getStripeSecretKey(), { apiVersion: '2022-08-01' }); @@ -21,7 +21,7 @@ export const handleWebhook = async (req: Request, res: Response) => { event = stripe.webhooks.constructEvent( req.body, sig, - infisical.get('STRIPE_WEBHOOK_SECRET')! + getStripeWebhookSecret() ); } catch (err) { Sentry.setUser({ email: req.user.email }); diff --git a/backend/src/helpers/auth.ts b/backend/src/helpers/auth.ts index 680120395..a08dcf2cc 100644 --- a/backend/src/helpers/auth.ts +++ b/backend/src/helpers/auth.ts @@ -1,5 +1,4 @@ import * as Sentry from '@sentry/node'; -import infisical from 'infisical-node'; import jwt from 'jsonwebtoken'; import bcrypt from 'bcrypt'; import { @@ -15,6 +14,12 @@ import { UnauthorizedRequestError, BadRequestError } from '../utils/errors'; +import { + getJwtAuthLifetime, + getJwtAuthSecret, + getJwtRefreshLifetime, + getJwtRefreshSecret +} from '../config'; /** * @@ -88,7 +93,7 @@ const getAuthUserPayload = async ({ let user; try { const decodedToken = ( - jwt.verify(authTokenValue, infisical.get('JWT_AUTH_SECRET')!) + jwt.verify(authTokenValue, getJwtAuthSecret()) ); user = await User.findOne({ @@ -219,16 +224,16 @@ const issueAuthTokens = async ({ userId }: { userId: string }) => { payload: { userId }, - expiresIn: infisical.get('JWT_AUTH_LIFETIME')!, - secret: infisical.get('JWT_AUTH_SECRET')! + expiresIn: getJwtAuthLifetime(), + secret: getJwtAuthSecret() }); refreshToken = createToken({ payload: { userId }, - expiresIn: infisical.get('JWT_REFRESH_LIFETIME')!, - secret: infisical.get('JWT_REFRESH_SECRET')! + expiresIn: getJwtRefreshLifetime(), + secret: getJwtRefreshSecret() }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/helpers/bot.ts b/backend/src/helpers/bot.ts index 8912befd1..5cfbeebf5 100644 --- a/backend/src/helpers/bot.ts +++ b/backend/src/helpers/bot.ts @@ -1,5 +1,4 @@ import * as Sentry from '@sentry/node'; -import infisical from 'infisical-node'; import { Bot, BotKey, @@ -14,6 +13,7 @@ import { decryptAsymmetric } from '../utils/crypto'; import { SECRET_SHARED } from '../variables'; +import { getEncryptionKey } from '../config'; /** * Create an inactive bot with name [name] for workspace with id [workspaceId] @@ -33,7 +33,7 @@ const createBot = async ({ const { publicKey, privateKey } = generateKeyPair(); const { ciphertext, iv, tag } = encryptSymmetric({ plaintext: privateKey, - key: infisical.get('ENCRYPTION_KEY')! + key: getEncryptionKey() }); bot = await new Bot({ @@ -130,7 +130,7 @@ const getKey = async ({ workspaceId }: { workspaceId: string }) => { ciphertext: bot.encryptedPrivateKey, iv: bot.iv, tag: bot.tag, - key: infisical.get('ENCRYPTION_KEY')! + key: getEncryptionKey() }); key = decryptAsymmetric({ diff --git a/backend/src/helpers/nodemailer.ts b/backend/src/helpers/nodemailer.ts index 45fce5d1d..d765f3200 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 { getSmtpFromName, getSmtpFromAddress } from '../config'; let smtpTransporter: nodemailer.Transporter; @@ -34,7 +34,7 @@ const sendMail = async ({ const htmlToSend = temp(substitutions); await smtpTransporter.sendMail({ - from: `"${infisical.get('SMTP_FROM_NAME')!}" <${infisical.get('SMTP_FROM_ADDRESS')!}>`, + from: `"${getSmtpFromName()}" <${getSmtpFromAddress()}>`, to: recipients.join(', '), subject: subjectLine, html: htmlToSend diff --git a/backend/src/helpers/organization.ts b/backend/src/helpers/organization.ts index 87711da92..fb559df1b 100644 --- a/backend/src/helpers/organization.ts +++ b/backend/src/helpers/organization.ts @@ -1,9 +1,14 @@ -import infisical from 'infisical-node'; import * as Sentry from '@sentry/node'; import Stripe from 'stripe'; import { Types } from 'mongoose'; import { ACCEPTED } from '../variables'; import { Organization, MembershipOrg } from '../models'; +import { + getStripeSecretKey, + getStripeProductPro, + getStripeProductTeam, + getStripeProductStarter +} from '../config'; /** * Create an organization with name [name] @@ -22,11 +27,11 @@ const createOrganization = async ({ let organization; try { // register stripe account - const stripe = new Stripe(infisical.get('STRIPE_SECRET_KEY')!, { + const stripe = new Stripe(getStripeSecretKey(), { apiVersion: '2022-08-01' }); - if (infisical.get('STRIPE_SECRET_KEY')) { + if (getStripeSecretKey()) { const customer = await stripe.customers.create({ email, description: name @@ -76,14 +81,14 @@ const initSubscriptionOrg = async ({ if (organization) { if (organization.customerId) { // initialize starter subscription with quantity of 0 - const stripe = new Stripe(infisical.get('STRIPE_SECRET_KEY')!, { + const stripe = new Stripe(getStripeSecretKey(), { apiVersion: '2022-08-01' }); const productToPriceMap = { - starter: infisical.get('STRIPE_PRODUCT_STARTER')!, - team: infisical.get('STRIPE_PRODUCT_TEAM')!, - pro: infisical.get('STRIPE_PRODUCT_PRO')! + starter: getStripeProductStarter(), + team: getStripeProductTeam(), + pro: getStripeProductPro() }; stripeSubscription = await stripe.subscriptions.create({ @@ -138,7 +143,7 @@ const updateSubscriptionOrgQuantity = async ({ status: ACCEPTED }); - const stripe = new Stripe(infisical.get('STRIPE_SECRET_KEY')!, { + const stripe = new Stripe(getStripeSecretKey(), { apiVersion: '2022-08-01' }); @@ -170,4 +175,4 @@ export { createOrganization, initSubscriptionOrg, updateSubscriptionOrgQuantity -}; +}; \ No newline at end of file diff --git a/backend/src/helpers/token.ts b/backend/src/helpers/token.ts index b2365cb2c..0f6a88cc9 100644 --- a/backend/src/helpers/token.ts +++ b/backend/src/helpers/token.ts @@ -1,5 +1,4 @@ import * as Sentry from '@sentry/node'; -import infisical from 'infisical-node'; import { Types } from 'mongoose'; import { TokenData } from '../models'; import crypto from 'crypto'; @@ -11,6 +10,7 @@ import { TOKEN_EMAIL_PASSWORD_RESET } from '../variables'; import { UnauthorizedRequestError } from '../utils/errors'; +import { getSaltRounds } from '../config'; /** * Create and store a token in the database for purpose [type] @@ -84,7 +84,7 @@ const createTokenHelper = async ({ const query: TokenDataQuery = { type }; const update: TokenDataUpdate = { type, - tokenHash: await bcrypt.hash(token, parseInt(infisical.get('SALT_ROUNDS')!) || 10), + tokenHash: await bcrypt.hash(token, getSaltRounds()), expiresAt } diff --git a/backend/src/index.ts b/backend/src/index.ts index 24172fc89..5647ad870 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -63,8 +63,17 @@ import { healthCheck } from './routes/status'; import { getLogger } from './utils/logger'; import { RouteNotFoundError } from './utils/errors'; import { requestErrorHandler } from './middleware/requestErrorHandler'; +import { + getMongoURL, + getNodeEnv, + getPort, + getSentryDSN, + getSiteURL +} from './config'; const main = async () => { + // TODO 1: handle case of empty string token + // TODO 2: handle case of undefined token const client = await infisical.connect({ token: process.env.INFISICAL_TOKEN!, debug: true @@ -73,13 +82,13 @@ const main = async () => { logTelemetryMessage(); setTransporter(initSmtp()); - await DatabaseService.initDatabase(infisical.get('MONGO_URL')!); - if (infisical.get('NODE_ENV') !== 'test') { + await DatabaseService.initDatabase(getMongoURL()); + if (getNodeEnv() !== 'test') { Sentry.init({ - dsn: infisical.get('SENTRY_DSN') as string, + dsn: getSentryDSN(), tracesSampleRate: 1.0, - debug: infisical.get('NODE_ENV') === 'production' ? false : true, - environment: infisical.get('NODE_ENV') as string + debug: getNodeEnv() === 'production' ? false : true, + environment: getNodeEnv() }); } @@ -91,13 +100,13 @@ const main = async () => { app.use( cors({ credentials: true, - origin: infisical.get('SITE_URL') as string + origin: getSiteURL() }) ); app.use(requestIp.mw()); - if (infisical.get('NODE_ENV') === 'production') { + if (getNodeEnv() === 'production') { // enable app-wide rate-limiting + helmet security // in production app.disable('x-powered-by'); @@ -157,9 +166,9 @@ const main = async () => { app.use(requestErrorHandler) - const server = app.listen(Number(infisical.get('PORT')) || 4000, () => { + const server = app.listen(getPort(), () => { createTestUserForDevelopment(); - getLogger("backend-main").info(`Server started listening at port ${Number(infisical.get('PORT')) || 4000}`) + getLogger("backend-main").info(`Server started listening at port ${getPort()}`) }); setUpHealthEndpoint(server); diff --git a/backend/src/integrations/exchange.ts b/backend/src/integrations/exchange.ts index 4e89e3aa9..1dccb03d0 100644 --- a/backend/src/integrations/exchange.ts +++ b/backend/src/integrations/exchange.ts @@ -1,4 +1,3 @@ -import infisical from 'infisical-node'; import * as Sentry from '@sentry/node'; import request from '../config/request'; import { @@ -15,6 +14,20 @@ import { INTEGRATION_GITHUB_TOKEN_URL, INTEGRATION_GITLAB_TOKEN_URL } from '../variables'; +import { + getSiteURL, + getClientIdAzure, + getClientSecretAzure, + getClientSecretHeroku, + getClientIdVercel, + getClientSecretVercel, + getClientIdNetlify, + getClientSecretNetlify, + getClientIdGitHub, + getClientSecretGitHub, + getClientIdGitLab, + getClientSecretGitLab +} from '../config'; interface ExchangeCodeAzureResponse { token_type: string; @@ -146,9 +159,9 @@ const exchangeCodeAzure = async ({ grant_type: 'authorization_code', code: code, scope: 'https://vault.azure.net/.default openid offline_access', - 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` + client_id: getClientIdAzure(), + client_secret: getClientSecretAzure(), + redirect_uri: `${getSiteURL()}/integrations/azure-key-vault/oauth2/callback` } as any) )).data; @@ -191,7 +204,7 @@ const exchangeCodeHeroku = async ({ new URLSearchParams({ grant_type: 'authorization_code', code: code, - client_secret: infisical.get('CLIENT_SECRET_HEROKU')! + client_secret: getClientSecretHeroku() } as any) )).data; @@ -229,9 +242,9 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => { INTEGRATION_VERCEL_TOKEN_URL, new URLSearchParams({ code: code, - client_id: infisical.get('CLIENT_ID_VERCEL')!, - client_secret: infisical.get('CLIENT_SECRET_VERCEL')!, - redirect_uri: `${infisical.get('SITE_URL')!}/integrations/vercel/oauth2/callback` + client_id: getClientIdVercel(), + client_secret: getClientSecretVercel(), + redirect_uri: `${getSiteURL()}/integrations/vercel/oauth2/callback` } as any) ) ).data; @@ -269,9 +282,9 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => { new URLSearchParams({ grant_type: 'authorization_code', code: code, - client_id: infisical.get('CLIENT_ID_NETLIFY')!, - client_secret: infisical.get('CLIENT_SECRET_NETLIFY')!, - redirect_uri: `${infisical.get('SITE_URL')!}/integrations/netlify/oauth2/callback` + client_id: getClientIdNetlify(), + client_secret: getClientSecretNetlify(), + redirect_uri: `${getSiteURL()}/integrations/netlify/oauth2/callback` } as any) ) ).data; @@ -320,10 +333,10 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => { res = ( await request.get(INTEGRATION_GITHUB_TOKEN_URL, { params: { - client_id: infisical.get('CLIENT_ID_GITHUB')!, - client_secret: infisical.get('CLIENT_SECRET_GITHUB')!, + client_id: getClientIdGitHub(), + client_secret: getClientSecretGitHub(), code: code, - redirect_uri: `${infisical.get('SITE_URL')!}/integrations/github/oauth2/callback` + redirect_uri: `${getSiteURL()}/integrations/github/oauth2/callback` }, headers: { 'Accept': 'application/json', @@ -366,9 +379,9 @@ const exchangeCodeGitlab = async ({ code }: { code: string }) => { new URLSearchParams({ grant_type: 'authorization_code', code: code, - client_id: infisical.get('CLIENT_ID_GITLAB')!, - client_secret: infisical.get('CLIENT_SECRET_GITLAB')!, - redirect_uri: `${infisical.get('SITE_URL')}/integrations/gitlab/oauth2/callback` + client_id: getClientIdGitLab(), + client_secret: getClientSecretGitLab(), + redirect_uri: `${getSiteURL()}/integrations/gitlab/oauth2/callback` } as any), { headers: { diff --git a/backend/src/integrations/refresh.ts b/backend/src/integrations/refresh.ts index 67cd382c1..a0aea080e 100644 --- a/backend/src/integrations/refresh.ts +++ b/backend/src/integrations/refresh.ts @@ -1,4 +1,3 @@ -import infisical from 'infisical-node'; import * as Sentry from '@sentry/node'; import request from '../config/request'; import { @@ -9,13 +8,6 @@ import { INTEGRATION_HEROKU, INTEGRATION_GITLAB, } from '../variables'; -// 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, @@ -24,6 +16,14 @@ import { import { IntegrationService } from '../services'; +import { + getSiteURL, + getClientIdAzure, + getClientSecretAzure, + getClientSecretHeroku, + getClientIdGitLab, + getClientSecretGitLab +} from '../config'; interface RefreshTokenAzureResponse { token_type: string; @@ -133,11 +133,11 @@ const exchangeRefreshAzure = async ({ const { data }: { data: RefreshTokenAzureResponse } = await request.post( INTEGRATION_AZURE_TOKEN_URL, new URLSearchParams({ - client_id: infisical.get('CLIENT_ID_AZURE')!, + client_id: getClientIdAzure(), scope: 'openid offline_access', refresh_token: refreshToken, grant_type: 'refresh_token', - client_secret: infisical.get('CLIENT_SECRET_AZURE')! + client_secret: getClientSecretAzure() } as any) ); @@ -180,7 +180,7 @@ const exchangeRefreshHeroku = async ({ new URLSearchParams({ grant_type: 'refresh_token', refresh_token: refreshToken, - client_secret: infisical.get('CLIENT_SECRET_HEROKU')! + client_secret: getClientSecretHeroku() } as any) ); @@ -223,9 +223,9 @@ const exchangeRefreshGitLab = async ({ new URLSearchParams({ grant_type: 'refresh_token', refresh_token: refreshToken, - client_id: infisical.get('CLIENT_ID_GITLAB')!, - client_secret: infisical.get('CLIENT_SECRET_GITLAB')!, - redirect_uri: `${infisical.get('SITE_URL')!}/integrations/gitlab/oauth2/callback` + client_id: getClientIdGitLab, + client_secret: getClientSecretGitLab(), + redirect_uri: `${getSiteURL()}/integrations/gitlab/oauth2/callback` } as any), { headers: { diff --git a/backend/src/middleware/requestErrorHandler.ts b/backend/src/middleware/requestErrorHandler.ts index 2a881e814..202a38e83 100644 --- a/backend/src/middleware/requestErrorHandler.ts +++ b/backend/src/middleware/requestErrorHandler.ts @@ -1,13 +1,13 @@ -import infisical from 'infisical-node'; import * as Sentry from '@sentry/node'; import { ErrorRequestHandler } from "express"; -import { InternalServerError, UnauthorizedRequestError } from "../utils/errors"; +import { InternalServerError } from "../utils/errors"; import { getLogger } from "../utils/logger"; import RequestError, { LogLevel } from "../utils/requestError"; +import { getNodeEnv } from '../config'; export const requestErrorHandler: ErrorRequestHandler = (error: RequestError | Error, req, res, next) => { if (res.headersSent) return next(); - if (infisical.get('NODE_ENV')! !== "production") { + if (getNodeEnv() !== "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 21b4763c2..7fb38ca25 100644 --- a/backend/src/middleware/requireMfaAuth.ts +++ b/backend/src/middleware/requireMfaAuth.ts @@ -1,8 +1,8 @@ -import infisical from 'infisical-node'; import jwt from 'jsonwebtoken'; import { Request, Response, NextFunction } from 'express'; import { User } from '../models'; import { BadRequestError, UnauthorizedRequestError } from '../utils/errors'; +import { getJwtMfaSecret } from '../config'; declare module 'jsonwebtoken' { export interface UserIDJwtPayload extends jwt.JwtPayload { @@ -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, infisical.get('JWT_MFA_SECRET')!) + jwt.verify(AUTH_TOKEN_VALUE, getJwtMfaSecret()) ); const user = await User.findOne({ diff --git a/backend/src/middleware/requireServiceTokenAuth.ts b/backend/src/middleware/requireServiceTokenAuth.ts index ff6bb7c59..106ca9bbb 100644 --- a/backend/src/middleware/requireServiceTokenAuth.ts +++ b/backend/src/middleware/requireServiceTokenAuth.ts @@ -1,8 +1,8 @@ -import infisical from 'infisical-node'; import jwt from 'jsonwebtoken'; import { Request, Response, NextFunction } from 'express'; import { ServiceToken } from '../models'; import { BadRequestError, UnauthorizedRequestError } from '../utils/errors'; +import { getJwtServiceSecret } from '../config'; // TODO: deprecate declare module 'jsonwebtoken' { @@ -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, infisical.get('JWT_SERVICE_SECRET')!) + jwt.verify(AUTH_TOKEN_VALUE, getJwtServiceSecret()) ); const serviceToken = await ServiceToken.findOne({ diff --git a/backend/src/middleware/requireSignupAuth.ts b/backend/src/middleware/requireSignupAuth.ts index deb08d9ae..19e6b3146 100644 --- a/backend/src/middleware/requireSignupAuth.ts +++ b/backend/src/middleware/requireSignupAuth.ts @@ -1,8 +1,8 @@ -import infisical from 'infisical-node'; import jwt from 'jsonwebtoken'; import { Request, Response, NextFunction } from 'express'; import { User } from '../models'; import { BadRequestError, UnauthorizedRequestError } from '../utils/errors'; +import { getJwtSignupSecret } from '../config'; declare module 'jsonwebtoken' { export interface UserIDJwtPayload extends jwt.JwtPayload { @@ -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, infisical.get('JWT_SIGNUP_SECRET')!) + jwt.verify(AUTH_TOKEN_VALUE, getJwtSignupSecret()) ); const user = await User.findOne({ diff --git a/backend/src/services/PostHogClient.ts b/backend/src/services/PostHogClient.ts index 2d1bb8391..15ccf0919 100644 --- a/backend/src/services/PostHogClient.ts +++ b/backend/src/services/PostHogClient.ts @@ -1,13 +1,17 @@ -import infisical from 'infisical-node'; import { PostHog } from 'posthog-node'; import { getLogger } from '../utils/logger'; +import { + getNodeEnv, + getTelemetryEnabled, + getPostHogProjectApiKey, + getPostHogHost +} from '../config'; /** * Logs telemetry enable/disable notice. */ const logTelemetryMessage = () => { - const TELEMETRY_ENABLED = infisical.get('TELEMETRY_ENABLED')! !== 'false' && true; - if(!TELEMETRY_ENABLED){ + if(!getTelemetryEnabled()){ getLogger("backend-main").info([ "", "To improve, Infisical collects telemetry data about general usage.", @@ -23,11 +27,10 @@ const logTelemetryMessage = () => { */ const getPostHogClient = () => { let postHogClient: any; - const TELEMETRY_ENABLED = infisical.get('TELEMETRY_ENABLED')! !== 'false' && true; - if (infisical.get('NODE_ENV') === 'production' && TELEMETRY_ENABLED) { + if (getNodeEnv() === 'production' && getTelemetryEnabled()) { // case: enable opt-out telemetry in production - postHogClient = new PostHog(infisical.get('POSTHOG_PROJECT_API_KEY')! || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE', { - host: infisical.get('POSTHOG_HOST')! + postHogClient = new PostHog(getPostHogProjectApiKey(), { + host: getPostHogHost() }); } diff --git a/backend/src/services/smtp.ts b/backend/src/services/smtp.ts index a9c056071..7a4ebf00b 100644 --- a/backend/src/services/smtp.ts +++ b/backend/src/services/smtp.ts @@ -1,4 +1,3 @@ -import infisical from 'infisical-node'; import nodemailer from 'nodemailer'; import { SMTP_HOST_SENDGRID, @@ -8,22 +7,29 @@ import { } from '../variables'; import SMTPConnection from 'nodemailer/lib/smtp-connection'; import * as Sentry from '@sentry/node'; +import { + getSmtpHost, + getSmtpUsername, + getSmtpPassword, + getSmtpSecure, + getSmtpPort +} from '../config'; export const initSmtp = () => { const mailOpts: SMTPConnection.Options = { - host: infisical.get('SMTP_HOST')!, - port: parseInt(infisical.get('SMTP_PORT')!) + host: getSmtpHost(), + port: getSmtpPort() }; - if (infisical.get('SMTP_USERNAME')! && infisical.get('SMTP_PASSWORD')!) { + if (getSmtpUsername() && getSmtpPassword()) { mailOpts.auth = { - user: infisical.get('SMTP_USERNAME')!, - pass: infisical.get('SMTP_PASSWORD')! + user: getSmtpUsername(), + pass: getSmtpPassword() }; } - if (infisical.get('SMTP_SECURE')! ? infisical.get('SMTP_SECURE')! === 'true' : false) { - switch (infisical.get('SMTP_HOST')!) { + if (getSmtpSecure() ? getSmtpSecure() : false) { + switch (getSmtpHost()) { case SMTP_HOST_SENDGRID: mailOpts.requireTLS = true; break; @@ -46,7 +52,7 @@ export const initSmtp = () => { } break; default: - if (infisical.get('SMTP_HOST')!.includes('amazonaws.com')) { + if (getSmtpHost().includes('amazonaws.com')) { mailOpts.tls = { ciphers: 'TLSv1.2' } @@ -67,7 +73,7 @@ export const initSmtp = () => { .catch((err) => { Sentry.setUser(null); Sentry.captureException( - `SMTP - Failed to connect to ${infisical.get('SMTP_HOST')!}:${infisical.get('SMTP_PORT')!} \n\t${err}` + `SMTP - Failed to connect to ${getSmtpHost()}:${getSmtpPort()} \n\t${err}` ); }); diff --git a/backend/src/utils/addDevelopmentUser.ts b/backend/src/utils/addDevelopmentUser.ts index ed886fd62..585740a6b 100644 --- a/backend/src/utils/addDevelopmentUser.ts +++ b/backend/src/utils/addDevelopmentUser.ts @@ -4,12 +4,12 @@ * ************************************************************************************************/ -import infisical from 'infisical-node'; import { Key, Membership, MembershipOrg, Organization, User, Workspace } from "../models"; import { Types } from 'mongoose'; +import { getNodeEnv } from '../config'; export const createTestUserForDevelopment = async () => { - if (infisical.get('NODE_ENV') === "development") { + if (getNodeEnv() === "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 590a9d950..ed29c97ca 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 { getLokiHost, getNodeEnv } 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(infisical.get('LOKI_HOST')! !== undefined){ + if(getLokiHost() !== undefined){ _transports.push( new LokiTransport({ - host: infisical.get('LOKI_HOST')!, + host: getLokiHost(), handleExceptions: true, handleRejections: true, batching: true, @@ -40,7 +40,7 @@ const createLoggerWithLabel = (level: string, label: string) => { labels: { app: process.env.npm_package_name, version: process.env.npm_package_version, - environment: infisical.get('NODE_ENV')! + environment: getNodeEnv() }, 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 7e7fd6070..4b5635bac 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 { getVerboseErrorOutput } from '../config'; export enum LogLevel { DEBUG = 100, @@ -87,8 +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){ + if(!getVerboseErrorOutput()){ _context = this._omit(_context, [ 'stacktrace', 'exception', diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index db8adf105..52bfcc614 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -1,4 +1,11 @@ -import infisical from 'infisical-node'; +import { + getClientIdHeroku, + getClientSlugVercel, + getClientIdNetlify, + getClientIdAzure, + getClientIdGitLab, + getClientIdGitHub +} from '../config'; // integrations const INTEGRATION_AZURE_KEY_VAULT = 'azure-key-vault'; @@ -57,7 +64,7 @@ const getIntegrationOptions = () => { image: 'Heroku.png', isAvailable: true, type: 'oauth', - clientId: infisical.get('CLIENT_ID_HEROKU')!, + clientId: getClientIdHeroku(), docsLink: '' }, { @@ -67,7 +74,7 @@ const getIntegrationOptions = () => { isAvailable: true, type: 'oauth', clientId: '', - clientSlug: infisical.get('CLIENT_SLUG_VERCEL')!, + clientSlug: getClientSlugVercel(), docsLink: '' }, { @@ -76,7 +83,7 @@ const getIntegrationOptions = () => { image: 'Netlify.png', isAvailable: true, type: 'oauth', - clientId: infisical.get('CLIENT_ID_NETLIFY')!, + clientId: getClientIdNetlify(), docsLink: '' }, { @@ -85,7 +92,7 @@ const getIntegrationOptions = () => { image: 'GitHub.png', isAvailable: true, type: 'oauth', - clientId: infisical.get('CLIENT_ID_GITHUB')!, + clientId: getClientIdGitHub(), docsLink: '' }, { @@ -130,7 +137,7 @@ const getIntegrationOptions = () => { image: 'Microsoft Azure.png', isAvailable: true, type: 'oauth', - clientId: infisical.get('CLIENT_ID_AZURE')!, + clientId: getClientIdAzure(), docsLink: '' }, { @@ -148,7 +155,7 @@ const getIntegrationOptions = () => { image: 'GitLab.png', isAvailable: true, type: 'custom', - clientId: infisical.get('CLIENT_ID_GITLAB'), + clientId: getClientIdGitLab(), docsLink: '' }, { diff --git a/docs/sdk/overview/usage.mdx b/docs/sdk/overview/usage.mdx index 2708f9a2b..84d963fff 100644 --- a/docs/sdk/overview/usage.mdx +++ b/docs/sdk/overview/usage.mdx @@ -59,17 +59,16 @@ infisical.connect({ Options: -| Option | Description | Default Value | -| -------------------- | ----------------------------------------------------------- | --------------------------- | -| `token` | ❗️ An Infisical Token to be used to fetch secrets | `None` | -| `siteURL` | Site URL of Infisical to connect to | `https://app.infisical.com` | -| `attachToProcessEnv` | Whether or not to attach fetched secrets to `process.env` | `False` | -| `defaultValues` | Default values for secrets if they aren't fetched/passed in | `{}` | +| Option | Description | Default Value | +| -------------------- | --------------------------------------------------------- | --------------------------- | +| `token` | ❗️ An Infisical Token to be used to fetch secrets | `None` | +| `siteURL` | Site URL of Infisical to connect to | `https://app.infisical.com` | +| `attachToProcessEnv` | Whether or not to attach fetched secrets to `process.env` | `false` | ## Access a Secret Value ```js -const dbURL = infisical.getSecretValue("DB_URL"); +const dbURL = infisical.get("DB_URL"); ``` ## Example with Express @@ -81,7 +80,7 @@ const infisical = require("infisical-node"); app.get("/", (req, res) => { // access value - const name = infisical.getSecret("NAME"); + const name = infisical.get("NAME"); res.send(`Hello! My name is: ${name}`); }); From b868b6a5f33b4e97b7f6ba9d897adc136d36b8c9 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Wed, 15 Mar 2023 14:03:39 +0700 Subject: [PATCH 3/7] Clean up infisical-node --- backend/package-lock.json | 14 ++--- backend/package.json | 2 +- backend/src/config/index.ts | 5 +- .../src/controllers/v2/signupController.ts | 6 +- backend/src/index.ts | 7 +-- frontend/package-lock.json | 63 +++++++++++++++---- frontend/package.json | 3 +- 7 files changed, 70 insertions(+), 30 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 5dbf691ca..1eb1dbc66 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -32,7 +32,7 @@ "express-validator": "^6.14.2", "handlebars": "^4.7.7", "helmet": "^5.1.1", - "infisical-node": "^1.0.34", + "infisical-node": "^1.0.37", "js-yaml": "^4.1.0", "jsonwebtoken": "^9.0.0", "jsrp": "^0.2.4", @@ -5975,9 +5975,9 @@ } }, "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==", + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/infisical-node/-/infisical-node-1.0.37.tgz", + "integrity": "sha512-9ZswN5UovZq46a7Qv/4KmfaAu9pO/TmxxdcEY2PosDIAlXbpfC651hcKr7T8q1iMlRnHLA/gpYkcZMeS0es0rg==", "dependencies": { "axios": "^1.3.3", "tweetnacl": "^1.0.3", @@ -16770,9 +16770,9 @@ "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==", + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/infisical-node/-/infisical-node-1.0.37.tgz", + "integrity": "sha512-9ZswN5UovZq46a7Qv/4KmfaAu9pO/TmxxdcEY2PosDIAlXbpfC651hcKr7T8q1iMlRnHLA/gpYkcZMeS0es0rg==", "requires": { "axios": "^1.3.3", "tweetnacl": "^1.0.3", diff --git a/backend/package.json b/backend/package.json index 04a5c0943..cd6cd214a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -23,7 +23,7 @@ "express-validator": "^6.14.2", "handlebars": "^4.7.7", "helmet": "^5.1.1", - "infisical-node": "^1.0.34", + "infisical-node": "^1.0.37", "js-yaml": "^4.1.0", "jsonwebtoken": "^9.0.0", "jsrp": "^0.2.4", diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index c4e072259..3fe935097 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,6 +1,6 @@ import infisical from 'infisical-node'; export const getPort = () => infisical.get('PORT')! || 4000; -export const getInviteOnlySignup = () => infisical.get('INVITE_ONLY_SIGNUP')! == undefined ? false : process.env.INVITE_ONLY_SIGNUP; +export const getInviteOnlySignup = () => infisical.get('INVITE_ONLY_SIGNUP')! == undefined ? false : infisical.get('INVITE_ONLY_SIGNUP'); export const getEncryptionKey = () => infisical.get('ENCRYPTION_KEY')!; export const getSaltRounds = () => parseInt(infisical.get('SALT_ROUNDS')!) || 10; export const getJwtAuthLifetime = () => infisical.get('JWT_AUTH_LIFETIME')! || '10d'; @@ -46,4 +46,5 @@ export const getStripeProductTeam = () => infisical.get('STRIPE_PRODUCT_TEAM')!; export const getStripePublishableKey = () => infisical.get('STRIPE_PUBLISHABLE_KEY')!; export const getStripeSecretKey = () => infisical.get('STRIPE_SECRET_KEY')!; export const getStripeWebhookSecret = () => infisical.get('STRIPE_WEBHOOK_SECRET')!; -export const getTelemetryEnabled = () => infisical.get('TELEMETRY_ENABLED')! !== 'false' && true; \ No newline at end of file +export const getTelemetryEnabled = () => infisical.get('TELEMETRY_ENABLED')! !== 'false' && true; +export const getLoopsApiKey = () => infisical.get('LOOPS_API_KEY')!; \ No newline at end of file diff --git a/backend/src/controllers/v2/signupController.ts b/backend/src/controllers/v2/signupController.ts index 3e4b1f44a..aaf055b78 100644 --- a/backend/src/controllers/v2/signupController.ts +++ b/backend/src/controllers/v2/signupController.ts @@ -8,7 +8,7 @@ import { import { issueAuthTokens } from '../../helpers/auth'; import { INVITED, ACCEPTED } from '../../variables'; import request from '../../config/request'; -import { getNodeEnv } from '../../config'; +import { getNodeEnv, getLoopsApiKey } from '../../config'; /** * Complete setting up user by adding their personal and auth information as part of the @@ -108,7 +108,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => { token = tokens.token; // sending a welcome email to new users - if (process.env.LOOPS_API_KEY) { + if (getLoopsApiKey()) { await request.post("https://app.loops.so/api/v1/events/send", { "email": email, "eventName": "Sign Up", @@ -117,7 +117,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => { }, { headers: { "Accept": "application/json", - "Authorization": "Bearer " + process.env.LOOPS_API_KEY + "Authorization": "Bearer " + getLoopsApiKey() }, }); } diff --git a/backend/src/index.ts b/backend/src/index.ts index 5647ad870..52740358d 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -72,11 +72,8 @@ import { } from './config'; const main = async () => { - // TODO 1: handle case of empty string token - // TODO 2: handle case of undefined token - const client = await infisical.connect({ - token: process.env.INFISICAL_TOKEN!, - debug: true + await infisical.connect({ + token: process.env.INFISICAL_TOKEN! }); logTelemetryMessage(); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5c92b590b..ad099a70d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,5 +1,5 @@ { - "name": "npm-proj-1677883018530-0.7603125731052582NtcmfK", + "name": "frontend", "lockfileVersion": 2, "requires": true, "packages": { @@ -46,6 +46,7 @@ "gray-matter": "^4.0.3", "http-proxy": "^1.18.1", "i18next": "^22.4.9", + "infisical-node": "^1.0.37", "jspdf": "^2.5.1", "jsrp": "^0.2.4", "markdown-it": "^13.0.1", @@ -13367,6 +13368,26 @@ "node": ">=8" } }, + "node_modules/infisical-node": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/infisical-node/-/infisical-node-1.0.37.tgz", + "integrity": "sha512-9ZswN5UovZq46a7Qv/4KmfaAu9pO/TmxxdcEY2PosDIAlXbpfC651hcKr7T8q1iMlRnHLA/gpYkcZMeS0es0rg==", + "dependencies": { + "axios": "^1.3.3", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + } + }, + "node_modules/infisical-node/node_modules/axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -17363,8 +17384,7 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/pump": { "version": "3.0.0", @@ -21761,9 +21781,9 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { - "version": "5.75.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", - "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -32078,6 +32098,28 @@ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, + "infisical-node": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/infisical-node/-/infisical-node-1.0.37.tgz", + "integrity": "sha512-9ZswN5UovZq46a7Qv/4KmfaAu9pO/TmxxdcEY2PosDIAlXbpfC651hcKr7T8q1iMlRnHLA/gpYkcZMeS0es0rg==", + "requires": { + "axios": "^1.3.3", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "dependencies": { + "axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + } + } + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -34869,8 +34911,7 @@ "proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "pump": { "version": "3.0.0", @@ -38113,9 +38154,9 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "webpack": { - "version": "5.75.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", - "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", diff --git a/frontend/package.json b/frontend/package.json index a1989701a..d53e40ca7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -39,8 +39,8 @@ "@reduxjs/toolkit": "^1.8.3", "@stripe/react-stripe-js": "^1.16.3", "@stripe/stripe-js": "^1.46.0", - "@types/argon2-browser": "^1.18.1", "@tanstack/react-query": "^4.23.0", + "@types/argon2-browser": "^1.18.1", "add": "^2.0.6", "argon2-browser": "^1.18.0", "axios": "^0.27.2", @@ -53,6 +53,7 @@ "gray-matter": "^4.0.3", "http-proxy": "^1.18.1", "i18next": "^22.4.9", + "infisical-node": "^1.0.37", "jspdf": "^2.5.1", "jsrp": "^0.2.4", "markdown-it": "^13.0.1", From db48ab8f6c81e5bc29b37aa4af4c636cb2ec5bfa Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Wed, 15 Mar 2023 15:16:50 +0700 Subject: [PATCH 4/7] Modify healthcheck.test --- backend/__tests__/healthcheck.test.ts | 11 ++++++++--- backend/src/index.ts | 3 ++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/backend/__tests__/healthcheck.test.ts b/backend/__tests__/healthcheck.test.ts index 234d2d8eb..945968437 100644 --- a/backend/__tests__/healthcheck.test.ts +++ b/backend/__tests__/healthcheck.test.ts @@ -1,11 +1,16 @@ -import { server } from '../src/app'; +import { Server } from 'http'; +import main from '../src'; import { describe, expect, it, beforeAll, afterAll } from '@jest/globals'; import supertest from 'supertest'; import { setUpHealthEndpoint } from '../src/services/health'; -const requestWithSupertest = supertest(server); +let requestWithSupertest: supertest.SuperTest; +let server: Server; + describe('Healthcheck endpoint', () => { beforeAll(async () => { + server = await main; + requestWithSupertest = supertest(server); setUpHealthEndpoint(server); }); afterAll(async () => { @@ -16,4 +21,4 @@ describe('Healthcheck endpoint', () => { const res = await requestWithSupertest.get('/healthcheck'); expect(res.status).toEqual(200); }); -}); +}); \ No newline at end of file diff --git a/backend/src/index.ts b/backend/src/index.ts index 52740358d..90589404d 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -169,6 +169,7 @@ const main = async () => { }); setUpHealthEndpoint(server); + return server; } -main(); \ No newline at end of file +export default main(); \ No newline at end of file From e6e3d82fa622a929d0e67be2ae7076620ae27662 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Wed, 15 Mar 2023 16:58:59 +0700 Subject: [PATCH 5/7] Modify healthcheck and server on-close to close database connection --- backend/__tests__/healthcheck.test.ts | 23 ++++++++++------------- backend/src/helpers/database.ts | 19 ++++++++++++++++++- backend/src/index.ts | 10 ++++++++-- backend/src/services/DatabaseService.ts | 18 +++++++++++++++++- 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/backend/__tests__/healthcheck.test.ts b/backend/__tests__/healthcheck.test.ts index 945968437..e054bd180 100644 --- a/backend/__tests__/healthcheck.test.ts +++ b/backend/__tests__/healthcheck.test.ts @@ -1,24 +1,21 @@ import { Server } from 'http'; import main from '../src'; import { describe, expect, it, beforeAll, afterAll } from '@jest/globals'; -import supertest from 'supertest'; -import { setUpHealthEndpoint } from '../src/services/health'; +import request from 'supertest'; -let requestWithSupertest: supertest.SuperTest; let server: Server; -describe('Healthcheck endpoint', () => { - beforeAll(async () => { - server = await main; - requestWithSupertest = supertest(server); - setUpHealthEndpoint(server); - }); - afterAll(async () => { - server.close(); - }); +beforeAll(async () => { + server = await main; +}); +afterAll(async () => { + server.close(); +}); + +describe('Healthcheck endpoint', () => { it('GET /healthcheck should return OK', async () => { - const res = await requestWithSupertest.get('/healthcheck'); + const res = await request(server).get('/healthcheck'); expect(res.status).toEqual(200); }); }); \ No newline at end of file diff --git a/backend/src/helpers/database.ts b/backend/src/helpers/database.ts index 9ba592ea3..9d128ea47 100644 --- a/backend/src/helpers/database.ts +++ b/backend/src/helpers/database.ts @@ -29,6 +29,23 @@ const initDatabaseHelper = async ({ return mongoose.connection; } +/** + * Close database conection + */ +const closeDatabaseHelper = async () => { + return Promise.all([ + new Promise((resolve) => { + if (mongoose.connection && mongoose.connection.readyState == 1) { + mongoose.connection.close() + .then(() => resolve('Database connection closed')); + } else { + resolve('Database connection already closed'); + } + }) + ]); +} + export { - initDatabaseHelper + initDatabaseHelper, + closeDatabaseHelper } \ No newline at end of file diff --git a/backend/src/index.ts b/backend/src/index.ts index 90589404d..64d58cfc5 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,3 +1,4 @@ +import mongoose from 'mongoose'; import dotenv from 'dotenv'; dotenv.config(); import infisical from 'infisical-node'; @@ -158,17 +159,22 @@ const main = async () => { //* 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` })) + next(RouteNotFoundError({ message: `The requested source '(${req.method})${req.url}' was not found` })) }) app.use(requestErrorHandler) const server = app.listen(getPort(), () => { - createTestUserForDevelopment(); getLogger("backend-main").info(`Server started listening at port ${getPort()}`) }); + createTestUserForDevelopment(); setUpHealthEndpoint(server); + + server.on('close', async () => { + await DatabaseService.closeDatabase(); + }) + return server; } diff --git a/backend/src/services/DatabaseService.ts b/backend/src/services/DatabaseService.ts index 2e8dc839f..fdfd7660a 100644 --- a/backend/src/services/DatabaseService.ts +++ b/backend/src/services/DatabaseService.ts @@ -1,16 +1,32 @@ import mongoose from 'mongoose'; import { getLogger } from '../utils/logger'; -import { initDatabaseHelper } from '../helpers/database'; +import { + initDatabaseHelper, + closeDatabaseHelper +} from '../helpers/database'; /** * Class to handle database actions */ class DatabaseService { + /** + * Initialize database connection + * @param {Object} obj + * @param {String} obj.mongoURL - mongo connection string + * @returns + */ static async initDatabase(MONGO_URL: string) { return await initDatabaseHelper({ mongoURL: MONGO_URL }); } + + /** + * Close database conection + */ + static async closeDatabase() { + return await closeDatabaseHelper(); + } } export default DatabaseService; \ No newline at end of file From d7054404007b88a417837d288a72a52a59545b1f Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Wed, 15 Mar 2023 18:21:52 +0700 Subject: [PATCH 6/7] Revamp Node SDK docs --- docs/mint.json | 13 ++-- docs/sdk/overview/usage.mdx | 103 ------------------------------- docs/sdks/overview/go.mdx | 8 +++ docs/sdks/overview/java.mdx | 8 +++ docs/sdks/overview/node.mdx | 111 ++++++++++++++++++++++++++++++++++ docs/sdks/overview/python.mdx | 8 +++ docs/sdks/overview/ruby.mdx | 8 +++ docs/sdks/overview/rust.mdx | 8 +++ 8 files changed, 160 insertions(+), 107 deletions(-) delete mode 100644 docs/sdk/overview/usage.mdx create mode 100644 docs/sdks/overview/go.mdx create mode 100644 docs/sdks/overview/java.mdx create mode 100644 docs/sdks/overview/node.mdx create mode 100644 docs/sdks/overview/python.mdx create mode 100644 docs/sdks/overview/ruby.mdx create mode 100644 docs/sdks/overview/rust.mdx diff --git a/docs/mint.json b/docs/mint.json index 697a7bfeb..efd669781 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -50,9 +50,9 @@ "url": "self-hosting" }, { - "name": "SDK", + "name": "SDKs", "icon": "puzzle-piece", - "url": "sdk" + "url": "sdks" }, { "name": "API Reference", @@ -192,9 +192,14 @@ ] }, { - "group": "SDK", + "group": "SDKs", "pages": [ - "sdk/overview/usage" + "sdks/overview/node", + "sdks/overview/python", + "sdks/overview/java", + "sdks/overview/ruby", + "sdks/overview/go", + "sdks/overview/rust" ] }, { diff --git a/docs/sdk/overview/usage.mdx b/docs/sdk/overview/usage.mdx deleted file mode 100644 index 84d963fff..000000000 --- a/docs/sdk/overview/usage.mdx +++ /dev/null @@ -1,103 +0,0 @@ ---- -title: "Usage" ---- - - - We're currently expanding the functionality of the Javascript SDK and working - on mirror SDKs for other languages like Python as well. Follow this GitHub - [issue](https://github.com/Infisical/infisical/issues/320) to stay updated. - - -Infisical provides a [Node SDK](https://github.com/Infisical/infisical-node) that users can easily install into their applications and use to fetch their secrets. - -With the SDK, users can currently fetch back secrets and define default values. - - - - -## Installation - -```bash -$ npm install infisical-node -``` - -## Import - -```js -// ES6 syntax -import infisical from "infisical-node"; - -// ES5 syntax -const infisical = require("infisical-node"); -``` - -## Initialization - -If your app only needs to connect to one Infisical project, you should use `infisical.connect`. If you need to connect to multiple Infisical projects, use `infisical.createConnection`. - -Both `connect` and `createConnection` take a parameter `token` and pull in the secrets accessible by that Infisical token. - -```js -// using async-await (recommended) -await infisical.connect({ - token: "your_infisical_token", -}); -``` - -```js -// using promise chaining -infisical.connect({ - token: "your_infisical_token" -}) -.then(() => { - console.log('Success!) -}) -.catch(err => { - console.error('Error: ', err); -}) -``` - -Options: - -| Option | Description | Default Value | -| -------------------- | --------------------------------------------------------- | --------------------------- | -| `token` | ❗️ An Infisical Token to be used to fetch secrets | `None` | -| `siteURL` | Site URL of Infisical to connect to | `https://app.infisical.com` | -| `attachToProcessEnv` | Whether or not to attach fetched secrets to `process.env` | `false` | - -## Access a Secret Value - -```js -const dbURL = infisical.get("DB_URL"); -``` - -## Example with Express - -```js -const express = require("express"); -const port = 3000; -const infisical = require("infisical-node"); - -app.get("/", (req, res) => { - // access value - const name = infisical.get("NAME"); - - res.send(`Hello! My name is: ${name}`); -}); - -app.listen(port, async () => { - // initialize client - await infisical.connect({ - token: "YOUR_INFISICAL_TOKEN", - }); - - console.log(`App listening on port ${port}`); -}); -``` - - - - Coming soon. - - - diff --git a/docs/sdks/overview/go.mdx b/docs/sdks/overview/go.mdx new file mode 100644 index 000000000..0ff2a2dde --- /dev/null +++ b/docs/sdks/overview/go.mdx @@ -0,0 +1,8 @@ +--- +title: "Go" +--- + +Coming soon. + +Follow this GitHub +[issue](https://github.com/Infisical/infisical/issues/436) to stay updated. diff --git a/docs/sdks/overview/java.mdx b/docs/sdks/overview/java.mdx new file mode 100644 index 000000000..2dd2c3c1d --- /dev/null +++ b/docs/sdks/overview/java.mdx @@ -0,0 +1,8 @@ +--- +title: "Java" +--- + +Coming soon. + +Follow this GitHub +[issue](https://github.com/Infisical/infisical/issues/434) to stay updated. diff --git a/docs/sdks/overview/node.mdx b/docs/sdks/overview/node.mdx new file mode 100644 index 000000000..37928933f --- /dev/null +++ b/docs/sdks/overview/node.mdx @@ -0,0 +1,111 @@ +--- +title: "Node" +--- + +If you're working with Node.js, the official [infisical-node](https://github.com/Infisical/infisical-node) package is the easiest way to fetch secrets for your application. + +## Installation + +Run `npm` to add `infisical` to your project. + +```bash +npm install infisical-node --save +``` + +## Initialization + +Set up `infisical` asynchronously as early as possible in your application by importing and initializing the global instance with `infisical.connect([options])`. + +This methods fetches back all the secrets in the project and environment accessible by the token passed in `options`. + + + + ```js + import infisical from "infisical-node"; + + const main = async () => { + await infisical.connect({ + token: "your_infisical_token", + }); + + // your app logic + } + + main(); + ``` + + + + ```js + const infisical = require("infisical-node"); + + infisical.connect({ + token: "your_infisical_token" + }) + .then(() => { + // your application logic + }) + .catch(err => { + console.error('Error: ', err); + }) + ```` + + + + +`infisical.connect([options])` + +| Option | Description | Default Value | +| --------- | -------------------------------------------------------------------------------------------- | --------------------------- | +| `token` | An [Infisical Token](/getting-started/dashboard/token) scoped to a project and environment | `None` | +| `siteURL` | Your self-hosted absolute site URL including the protocol (e.g. `https://app.infisical.com`) | `https://app.infisical.com` | +| `debug` | Whether or not debug mode is on | `false` | + + + If you need to connect to multiple Infisical projects, you can use + `infisical.createConnection([options])` to return a local instance of + `infisical`. + + +## Usage + +To get the value of secret, pass the name of its key into `infisical.get()`. + +```js +const value = infisical.get("SOME_KEY"); +``` + + + `infisical` falls back to `process.env` if `token` is `undefined` during + initialization or if a value is not found in the secrets fetched. + + +## Example with Express + +```js +const express = require("express"); +const port = 3000; +const infisical = require("infisical-node"); + +const main = async () => { + await infisical.connect({ + token: "st.xxx.xxx", + }); + + // your application logic + + app.get("/", (req, res) => { + res.send(`Howdy, ${infisical.get("NAME")}!`); + }); + + app.listen(port, async () => { + console.log(`App listening on port ${port}`); + }); +}; +``` + + + We do not recommend hardcoding your [Infisical + Token](/getting-started/dashboard/token). Setting it as an environment + variable would be best. + diff --git a/docs/sdks/overview/python.mdx b/docs/sdks/overview/python.mdx new file mode 100644 index 000000000..3a9b82152 --- /dev/null +++ b/docs/sdks/overview/python.mdx @@ -0,0 +1,8 @@ +--- +title: "Python" +--- + +Coming soon. + +Follow this GitHub +[issue](https://github.com/Infisical/infisical/issues/433) to stay updated. diff --git a/docs/sdks/overview/ruby.mdx b/docs/sdks/overview/ruby.mdx new file mode 100644 index 000000000..80dab508a --- /dev/null +++ b/docs/sdks/overview/ruby.mdx @@ -0,0 +1,8 @@ +--- +title: "Ruby" +--- + +Coming soon. + +Follow this GitHub +[issue](https://github.com/Infisical/infisical/issues/435) to stay updated. diff --git a/docs/sdks/overview/rust.mdx b/docs/sdks/overview/rust.mdx new file mode 100644 index 000000000..8fa9b3b5b --- /dev/null +++ b/docs/sdks/overview/rust.mdx @@ -0,0 +1,8 @@ +--- +title: "Rust" +--- + +Coming soon. + +Follow this GitHub +[issue](https://github.com/Infisical/infisical/issues/437) to stay updated. From b591d638d0e61d1f8dc9ba9434cc47972e0a45e9 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Thu, 16 Mar 2023 00:28:29 +0700 Subject: [PATCH 7/7] Update Node SDK docs --- docs/sdks/overview/node.mdx | 87 +++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 22 deletions(-) diff --git a/docs/sdks/overview/node.mdx b/docs/sdks/overview/node.mdx index 37928933f..58002392f 100644 --- a/docs/sdks/overview/node.mdx +++ b/docs/sdks/overview/node.mdx @@ -6,7 +6,7 @@ If you're working with Node.js, the official [infisical-node](https://github.com ## Installation -Run `npm` to add `infisical` to your project. +Run `npm` to add `infisical-node` to your project. ```bash npm install infisical-node --save @@ -14,10 +14,63 @@ npm install infisical-node --save ## Initialization -Set up `infisical` asynchronously as early as possible in your application by importing and initializing the global instance with `infisical.connect([options])`. +Set up the Infisical client asynchronously as early as possible in your application by importing and initializing the global instance with `infisical.connect(options)`. This methods fetches back all the secrets in the project and environment accessible by the token passed in `options`. +### infisical.connect(options) + +Updates the global instance of the Infisical client with a connection to an Infisical project and fetches back secrets if supplied with an [Infisical Token](/getting-started/dashboard/token). + + + + + An [Infisical Token](/getting-started/dashboard/token) scoped to a project + and environment + + + Your self-hosted absolute site URL including the protocol (e.g. + `https://app.infisical.com`) + + + Whether or not debug mode is on + + + Whether or not to attach fetched secrets to `process.env` + + + + +### infisical.createConnection(options) + +Returns a local instance of the Infisical client with a connection to an Infisical project and fetches back secrets if supplied with an [Infisical Token](/getting-started/dashboard/token). + +This method is useful if you wish to connect to two or more Infisical projects within your app. + + + + + An [Infisical Token](/getting-started/dashboard/token) scoped to a project + and environment + + + Your self-hosted absolute site URL including the protocol (e.g. + `https://app.infisical.com`) + + + Whether or not debug mode is on + + + + ```js @@ -53,33 +106,23 @@ This methods fetches back all the secrets in the project and environment accessi -`infisical.connect([options])` - -| Option | Description | Default Value | -| --------- | -------------------------------------------------------------------------------------------- | --------------------------- | -| `token` | An [Infisical Token](/getting-started/dashboard/token) scoped to a project and environment | `None` | -| `siteURL` | Your self-hosted absolute site URL including the protocol (e.g. `https://app.infisical.com`) | `https://app.infisical.com` | -| `debug` | Whether or not debug mode is on | `false` | - - - If you need to connect to multiple Infisical projects, you can use - `infisical.createConnection([options])` to return a local instance of - `infisical`. - - ## Usage -To get the value of secret, pass the name of its key into `infisical.get()`. +To get the value of a secret, use `infisical.get(key)`. + +### infisical.get(key) + +Return the value of the secret with the specified `key`. Note that the Infisical client falls back to `process.env` if `token` is `undefined` during the +initialization step or if a value for the secret is not found in the fetched secrets. + + + The key of the secret + ```js const value = infisical.get("SOME_KEY"); ``` - - `infisical` falls back to `process.env` if `token` is `undefined` during - initialization or if a value is not found in the secrets fetched. - - ## Example with Express ```js