Merge pull request #432 from Infisical/add-infisical-node

Add the new infisical-node SDK to the backend
This commit is contained in:
BlackMagiq
2023-03-16 00:31:12 +07:00
committed by GitHub
60 changed files with 1013 additions and 833 deletions
-3
View File
@@ -16,9 +16,6 @@ JWT_AUTH_LIFETIME=
JWT_REFRESH_LIFETIME= JWT_REFRESH_LIFETIME=
JWT_SIGNUP_LIFETIME= JWT_SIGNUP_LIFETIME=
# Optional lifetimes for OTP expressed in seconds
EMAIL_TOKEN_LIFETIME=
# MongoDB # MongoDB
# Backend will connect to the MongoDB instance at connection string MONGO_URL which can either be a ref # 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 # to the MongoDB container instance or Mongo Cloud
+14 -12
View File
@@ -1,19 +1,21 @@
import { server } from '../src/app'; import { Server } from 'http';
import main from '../src';
import { describe, expect, it, beforeAll, afterAll } from '@jest/globals'; import { describe, expect, it, beforeAll, afterAll } from '@jest/globals';
import supertest from 'supertest'; import request from 'supertest';
import { setUpHealthEndpoint } from '../src/services/health';
let server: Server;
beforeAll(async () => {
server = await main;
});
afterAll(async () => {
server.close();
});
const requestWithSupertest = supertest(server);
describe('Healthcheck endpoint', () => { describe('Healthcheck endpoint', () => {
beforeAll(async () => {
setUpHealthEndpoint(server);
});
afterAll(async () => {
server.close();
});
it('GET /healthcheck should return OK', async () => { 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); expect(res.status).toEqual(200);
}); });
}); });
-1
View File
@@ -4,7 +4,6 @@ declare global {
namespace NodeJS { namespace NodeJS {
interface ProcessEnv { interface ProcessEnv {
PORT: string; PORT: string;
EMAIL_TOKEN_LIFETIME: string;
ENCRYPTION_KEY: string; ENCRYPTION_KEY: string;
SALT_ROUNDS: string; SALT_ROUNDS: string;
JWT_AUTH_LIFETIME: string; JWT_AUTH_LIFETIME: string;
+21
View File
@@ -32,6 +32,7 @@
"express-validator": "^6.14.2", "express-validator": "^6.14.2",
"handlebars": "^4.7.7", "handlebars": "^4.7.7",
"helmet": "^5.1.1", "helmet": "^5.1.1",
"infisical-node": "^1.0.37",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.0",
"jsrp": "^0.2.4", "jsrp": "^0.2.4",
@@ -5973,6 +5974,16 @@
"node": ">=0.8.19" "node": ">=0.8.19"
} }
}, },
"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/inflight": { "node_modules/inflight": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -16758,6 +16769,16 @@
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true "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"
}
},
"inflight": { "inflight": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+1
View File
@@ -23,6 +23,7 @@
"express-validator": "^6.14.2", "express-validator": "^6.14.2",
"handlebars": "^4.7.7", "handlebars": "^4.7.7",
"helmet": "^5.1.1", "helmet": "^5.1.1",
"infisical-node": "^1.0.37",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.0",
"jsrp": "^0.2.4", "jsrp": "^0.2.4",
-144
View File
@@ -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}`)
});
+50 -105
View File
@@ -1,105 +1,50 @@
const PORT = process.env.PORT || 4000; import infisical from 'infisical-node';
const EMAIL_TOKEN_LIFETIME = parseInt(process.env.EMAIL_TOKEN_LIFETIME! || '86400'); export const getPort = () => infisical.get('PORT')! || 4000;
const INVITE_ONLY_SIGNUP = process.env.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');
const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY!; export const getEncryptionKey = () => infisical.get('ENCRYPTION_KEY')!;
const SALT_ROUNDS = parseInt(process.env.SALT_ROUNDS!) || 10; export const getSaltRounds = () => parseInt(infisical.get('SALT_ROUNDS')!) || 10;
const JWT_AUTH_LIFETIME = process.env.JWT_AUTH_LIFETIME! || '10d'; export const getJwtAuthLifetime = () => infisical.get('JWT_AUTH_LIFETIME')! || '10d';
const JWT_AUTH_SECRET = process.env.JWT_AUTH_SECRET!; export const getJwtAuthSecret = () => infisical.get('JWT_AUTH_SECRET')!;
const JWT_MFA_LIFETIME = process.env.JWT_MFA_LIFETIME! || '5m'; export const getJwtMfaLifetime = () => infisical.get('JWT_MFA_LIFETIME')!;
const JWT_MFA_SECRET = process.env.JWT_MFA_SECRET!; export const getJwtMfaSecret = () => infisical.get('JWT_MFA_LIFETIME')! || '5m';
const JWT_REFRESH_LIFETIME = process.env.JWT_REFRESH_LIFETIME! || '90d'; export const getJwtRefreshLifetime = () => infisical.get('JWT_REFRESH_LIFETIME')! || '90d';
const JWT_REFRESH_SECRET = process.env.JWT_REFRESH_SECRET!; export const getJwtRefreshSecret = () => infisical.get('JWT_REFRESH_SECRET')!;
const JWT_SERVICE_SECRET = process.env.JWT_SERVICE_SECRET!; export const getJwtServiceSecret = () => infisical.get('JWT_SERVICE_SECRET')!;
const JWT_SIGNUP_LIFETIME = process.env.JWT_SIGNUP_LIFETIME! || '15m'; export const getJwtSignupLifetime = () => infisical.get('JWT_SIGNUP_LIFETIME')!;
const JWT_SIGNUP_SECRET = process.env.JWT_SIGNUP_SECRET!; export const getJwtSignupSecret = () => infisical.get('JWT_SIGNUP_SECRET')!;
const MONGO_URL = process.env.MONGO_URL!; export const getMongoURL = () => infisical.get('MONGO_URL')!;
const NODE_ENV = process.env.NODE_ENV! || 'production'; export const getNodeEnv = () => infisical.get('NODE_ENV')!;
const VERBOSE_ERROR_OUTPUT = process.env.VERBOSE_ERROR_OUTPUT! === 'true' && true; export const getVerboseErrorOutput = () => infisical.get('VERBOSE_ERROR_OUTPUT')! === 'true' && true;
const LOKI_HOST = process.env.LOKI_HOST || undefined; export const getLokiHost = () => infisical.get('LOKI_HOST')!;
const CLIENT_ID_AZURE = process.env.CLIENT_ID_AZURE!; export const getClientIdAzure = () => infisical.get('CLIENT_ID_AZURE')!;
const CLIENT_ID_HEROKU = process.env.CLIENT_ID_HEROKU!; export const getClientIdHeroku = () => infisical.get('CLIENT_ID_HEROKU')!;
const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!; export const getClientIdVercel = () => infisical.get('CLIENT_ID_VERCEL')!;
const CLIENT_ID_NETLIFY = process.env.CLIENT_ID_NETLIFY!; export const getClientIdNetlify = () => infisical.get('CLIENT_ID_NETLIFY')!;
const CLIENT_ID_GITHUB = process.env.CLIENT_ID_GITHUB!; export const getClientIdGitHub = () => infisical.get('CLIENT_ID_GITHUB')!;
const CLIENT_ID_GITLAB = process.env.CLIENT_ID_GITLAB!; export const getClientIdGitLab = () => infisical.get('CLIENT_ID_GITLAB')!;
const CLIENT_SECRET_AZURE = process.env.CLIENT_SECRET_AZURE!; export const getClientSecretAzure = () => infisical.get('CLIENT_SECRET_AZURE')!;
const CLIENT_SECRET_HEROKU = process.env.CLIENT_SECRET_HEROKU!; export const getClientSecretHeroku = () => infisical.get('CLIENT_SECRET_HEROKU')!;
const CLIENT_SECRET_VERCEL = process.env.CLIENT_SECRET_VERCEL!; export const getClientSecretVercel = () => infisical.get('CLIENT_SECRET_VERCEL')!;
const CLIENT_SECRET_NETLIFY = process.env.CLIENT_SECRET_NETLIFY!; export const getClientSecretNetlify = () => infisical.get('CLIENT_SECRET_NETLIFY')!;
const CLIENT_SECRET_GITHUB = process.env.CLIENT_SECRET_GITHUB!; export const getClientSecretGitHub = () => infisical.get('CLIENT_SECRET_GITHUB')!;
const CLIENT_SECRET_GITLAB = process.env.CLIENT_SECRET_GITLAB; export const getClientSecretGitLab = () => infisical.get('CLIENT_SECRET_GITLAB')!;
const CLIENT_SLUG_VERCEL = process.env.CLIENT_SLUG_VERCEL!; export const getClientSlugVercel = () => infisical.get('CLIENT_SLUG_VERCEL')!;
const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com'; export const getPostHogHost = () => infisical.get('POSTHOG_HOST')! || 'https://app.posthog.com';
const POSTHOG_PROJECT_API_KEY = export const getPostHogProjectApiKey = () => infisical.get('POSTHOG_PROJECT_API_KEY')! || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE';
process.env.POSTHOG_PROJECT_API_KEY! || export const getSentryDSN = () => infisical.get('SENTRY_DSN')!;
'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE'; export const getSiteURL = () => infisical.get('SITE_URL')!;
const SENTRY_DSN = process.env.SENTRY_DSN!; export const getSmtpHost = () => infisical.get('SMTP_HOST')!;
const SITE_URL = process.env.SITE_URL!; export const getSmtpSecure = () => infisical.get('SMTP_SECURE')! === 'true' || false;
const SMTP_HOST = process.env.SMTP_HOST!; export const getSmtpPort = () => parseInt(infisical.get('SMTP_PORT')!) || 587;
const SMTP_SECURE = process.env.SMTP_SECURE! === 'true' || false; export const getSmtpUsername = () => infisical.get('SMTP_USERNAME')!;
const SMTP_PORT = parseInt(process.env.SMTP_PORT!) || 587; export const getSmtpPassword = () => infisical.get('SMTP_PASSWORD')!;
const SMTP_USERNAME = process.env.SMTP_USERNAME!; export const getSmtpFromAddress = () => infisical.get('SMTP_FROM_ADDRESS')!;
const SMTP_PASSWORD = process.env.SMTP_PASSWORD!; export const getSmtpFromName = () => infisical.get('SMTP_FROM_NAME')! || 'Infisical';
const SMTP_FROM_ADDRESS = process.env.SMTP_FROM_ADDRESS!; export const getStripeProductStarter = () => infisical.get('STRIPE_PRODUCT_STARTER')!;
const SMTP_FROM_NAME = process.env.SMTP_FROM_NAME! || 'Infisical'; export const getStripeProductPro = () => infisical.get('STRIPE_PRODUCT_PRO')!;
const STRIPE_PRODUCT_STARTER = process.env.STRIPE_PRODUCT_STARTER!; export const getStripeProductTeam = () => infisical.get('STRIPE_PRODUCT_TEAM')!;
const STRIPE_PRODUCT_PRO = process.env.STRIPE_PRODUCT_PRO!; export const getStripePublishableKey = () => infisical.get('STRIPE_PUBLISHABLE_KEY')!;
const STRIPE_PRODUCT_TEAM = process.env.STRIPE_PRODUCT_TEAM!; export const getStripeSecretKey = () => infisical.get('STRIPE_SECRET_KEY')!;
const STRIPE_PUBLISHABLE_KEY = process.env.STRIPE_PUBLISHABLE_KEY!; export const getStripeWebhookSecret = () => infisical.get('STRIPE_WEBHOOK_SECRET')!;
const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY!; export const getTelemetryEnabled = () => infisical.get('TELEMETRY_ENABLED')! !== 'false' && true;
const STRIPE_WEBHOOK_SECRET = process.env.STRIPE_WEBHOOK_SECRET!; export const getLoopsApiKey = () => infisical.get('LOOPS_API_KEY')!;
const TELEMETRY_ENABLED = process.env.TELEMETRY_ENABLED! !== 'false' && true;
const LICENSE_KEY = process.env.LICENSE_KEY!;
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
};
+13 -13
View File
@@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-var-requires */ import * as Sentry from '@sentry/node';
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import * as Sentry from '@sentry/node';
import * as bigintConversion from 'bigint-conversion'; import * as bigintConversion from 'bigint-conversion';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const jsrp = require('jsrp'); const jsrp = require('jsrp');
import { User, LoginSRPDetail } from '../../models'; import { User, LoginSRPDetail } from '../../models';
import { createToken, issueAuthTokens, clearTokens } from '../../helpers/auth'; import { createToken, issueAuthTokens, clearTokens } from '../../helpers/auth';
@@ -11,15 +11,15 @@ import {
ACTION_LOGIN, ACTION_LOGIN,
ACTION_LOGOUT ACTION_LOGOUT
} from '../../variables'; } from '../../variables';
import {
NODE_ENV,
JWT_AUTH_LIFETIME,
JWT_AUTH_SECRET,
JWT_REFRESH_SECRET
} from '../../config';
import { BadRequestError } from '../../utils/errors'; import { BadRequestError } from '../../utils/errors';
import { EELogService } from '../../ee/services'; import { EELogService } from '../../ee/services';
import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this
import {
getNodeEnv,
getJwtRefreshSecret,
getJwtAuthLifetime,
getJwtAuthSecret
} from '../../config';
declare module 'jsonwebtoken' { declare module 'jsonwebtoken' {
export interface UserIDJwtPayload extends jwt.JwtPayload { export interface UserIDJwtPayload extends jwt.JwtPayload {
@@ -126,7 +126,7 @@ export const login2 = async (req: Request, res: Response) => {
httpOnly: true, httpOnly: true,
path: '/', path: '/',
sameSite: 'strict', sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false secure: getNodeEnv() === 'production' ? true : false
}); });
const loginAction = await EELogService.createAction({ const loginAction = await EELogService.createAction({
@@ -182,7 +182,7 @@ export const logout = async (req: Request, res: Response) => {
httpOnly: true, httpOnly: true,
path: '/', path: '/',
sameSite: 'strict', sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false secure: getNodeEnv() === 'production' ? true : false
}); });
const logoutAction = await EELogService.createAction({ const logoutAction = await EELogService.createAction({
@@ -237,7 +237,7 @@ export const getNewToken = async (req: Request, res: Response) => {
} }
const decodedToken = <jwt.UserIDJwtPayload>( const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(refreshToken, JWT_REFRESH_SECRET) jwt.verify(refreshToken, getJwtRefreshSecret())
); );
const user = await User.findOne({ const user = await User.findOne({
@@ -252,8 +252,8 @@ export const getNewToken = async (req: Request, res: Response) => {
payload: { payload: {
userId: decodedToken.userId userId: decodedToken.userId
}, },
expiresIn: JWT_AUTH_LIFETIME, expiresIn: getJwtAuthLifetime(),
secret: JWT_AUTH_SECRET secret: getJwtAuthSecret()
}); });
return res.status(200).send({ return res.status(200).send({
@@ -5,7 +5,7 @@ import {
IntegrationAuth, IntegrationAuth,
Bot Bot
} from '../../models'; } from '../../models';
import { INTEGRATION_SET, INTEGRATION_OPTIONS } from '../../variables'; import { INTEGRATION_SET, getIntegrationOptions as getIntegrationOptionsFunc } from '../../variables';
import { IntegrationService } from '../../services'; import { IntegrationService } from '../../services';
import { import {
getApps, getApps,
@@ -39,9 +39,11 @@ export const getIntegrationAuth = async (req: Request, res: Response) => {
} }
export const getIntegrationOptions = async (req: Request, res: Response) => { export const getIntegrationOptions = async (req: Request, res: Response) => {
return res.status(200).send({ const INTEGRATION_OPTIONS = getIntegrationOptionsFunc();
integrationOptions: INTEGRATION_OPTIONS,
}); return res.status(200).send({
integrationOptions: INTEGRATION_OPTIONS,
});
}; };
/** /**
@@ -1,13 +1,13 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/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 { import {
findMembership, findMembership,
deleteMembership as deleteMember deleteMembership as deleteMember
} from '../../helpers/membership'; } from '../../helpers/membership';
import { sendMail } from '../../helpers/nodemailer'; import { sendMail } from '../../helpers/nodemailer';
import { SITE_URL } from '../../config';
import { ADMIN, MEMBER, ACCEPTED } from '../../variables'; import { ADMIN, MEMBER, ACCEPTED } from '../../variables';
import { getSiteURL } from '../../config';
/** /**
* Check that user is a member of workspace with id [workspaceId] * 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, inviterFirstName: req.user.firstName,
inviterEmail: req.user.email, inviterEmail: req.user.email,
workspaceName: req.membership.workspace.name, workspaceName: req.membership.workspace.name,
callback_url: SITE_URL + '/login' callback_url: getSiteURL() + '/login'
} }
}); });
} catch (err) { } catch (err) {
@@ -1,6 +1,5 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { SITE_URL, JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET } from '../../config';
import { MembershipOrg, Organization, User } from '../../models'; import { MembershipOrg, Organization, User } from '../../models';
import { deleteMembershipOrg as deleteMemberFromOrg } from '../../helpers/membershipOrg'; import { deleteMembershipOrg as deleteMemberFromOrg } from '../../helpers/membershipOrg';
import { createToken } from '../../helpers/auth'; import { createToken } from '../../helpers/auth';
@@ -8,6 +7,7 @@ import { updateSubscriptionOrgQuantity } from '../../helpers/organization';
import { sendMail } from '../../helpers/nodemailer'; import { sendMail } from '../../helpers/nodemailer';
import { TokenService } from '../../services'; import { TokenService } from '../../services';
import { OWNER, ADMIN, MEMBER, ACCEPTED, INVITED, TOKEN_EMAIL_ORG_INVITATION } from '../../variables'; 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 * Delete organization membership with id [membershipOrgId] from organization
@@ -178,7 +178,7 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
organizationName: organization.name, organizationName: organization.name,
email: inviteeEmail, email: inviteeEmail,
token, token,
callback_url: SITE_URL + '/signupinvite' callback_url: getSiteURL() + '/signupinvite'
} }
}); });
} }
@@ -250,8 +250,8 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
payload: { payload: {
userId: user._id.toString() userId: user._id.toString()
}, },
expiresIn: JWT_SIGNUP_LIFETIME, expiresIn: getJwtSignupLifetime(),
secret: JWT_SIGNUP_SECRET secret: getJwtSignupSecret()
}); });
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
@@ -1,26 +1,18 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { import { Request, Response } from 'express';
SITE_URL,
STRIPE_SECRET_KEY
} from '../../config';
import Stripe from 'stripe'; import Stripe from 'stripe';
const stripe = new Stripe(STRIPE_SECRET_KEY, {
apiVersion: '2022-08-01'
});
import { import {
Membership, Membership,
MembershipOrg, MembershipOrg,
Organization, Organization,
Workspace, Workspace,
IncidentContactOrg, IncidentContactOrg
IMembershipOrg
} from '../../models'; } from '../../models';
import { createOrganization as create } from '../../helpers/organization'; import { createOrganization as create } from '../../helpers/organization';
import { addMembershipsOrg } from '../../helpers/membershipOrg'; import { addMembershipsOrg } from '../../helpers/membershipOrg';
import { OWNER, ACCEPTED } from '../../variables'; import { OWNER, ACCEPTED } from '../../variables';
import _ from 'lodash'; import _ from 'lodash';
import { getStripeSecretKey, getSiteURL } from '../../config';
export const getOrganizations = async (req: Request, res: Response) => { export const getOrganizations = async (req: Request, res: Response) => {
let organizations; let organizations;
@@ -325,6 +317,10 @@ export const createOrganizationPortalSession = async (
) => { ) => {
let session; let session;
try { try {
const stripe = new Stripe(getStripeSecretKey(), {
apiVersion: '2022-08-01'
});
// check if there is a payment method on file // check if there is a payment method on file
const paymentMethods = await stripe.paymentMethods.list({ const paymentMethods = await stripe.paymentMethods.list({
customer: req.membershipOrg.organization.customerId, customer: req.membershipOrg.organization.customerId,
@@ -337,13 +333,13 @@ export const createOrganizationPortalSession = async (
customer: req.membershipOrg.organization.customerId, customer: req.membershipOrg.organization.customerId,
mode: 'setup', mode: 'setup',
payment_method_types: ['card'], payment_method_types: ['card'],
success_url: SITE_URL + '/dashboard', success_url: getSiteURL() + '/dashboard',
cancel_url: SITE_URL + '/dashboard' cancel_url: getSiteURL() + '/dashboard'
}); });
} else { } else {
session = await stripe.billingPortal.sessions.create({ session = await stripe.billingPortal.sessions.create({
customer: req.membershipOrg.organization.customerId, customer: req.membershipOrg.organization.customerId,
return_url: SITE_URL + '/dashboard' return_url: getSiteURL() + '/dashboard'
}); });
} }
@@ -369,6 +365,10 @@ export const getOrganizationSubscriptions = async (
) => { ) => {
let subscriptions; let subscriptions;
try { try {
const stripe = new Stripe(getStripeSecretKey(), {
apiVersion: '2022-08-01'
});
subscriptions = await stripe.subscriptions.list({ subscriptions = await stripe.subscriptions.list({
customer: req.membershipOrg.organization.customerId customer: req.membershipOrg.organization.customerId
}); });
@@ -7,9 +7,9 @@ import { User, BackupPrivateKey, LoginSRPDetail } from '../../models';
import { createToken } from '../../helpers/auth'; import { createToken } from '../../helpers/auth';
import { sendMail } from '../../helpers/nodemailer'; import { sendMail } from '../../helpers/nodemailer';
import { TokenService } from '../../services'; import { TokenService } from '../../services';
import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, SITE_URL } from '../../config';
import { TOKEN_EMAIL_PASSWORD_RESET } from '../../variables'; import { TOKEN_EMAIL_PASSWORD_RESET } from '../../variables';
import { BadRequestError } from '../../utils/errors'; import { BadRequestError } from '../../utils/errors';
import { getSiteURL, getJwtSignupLifetime, getJwtSignupSecret } from '../../config';
/** /**
* Password reset step 1: Send email verification link to email [email] * Password reset step 1: Send email verification link to email [email]
@@ -44,7 +44,7 @@ export const emailPasswordReset = async (req: Request, res: Response) => {
substitutions: { substitutions: {
email, email,
token, token,
callback_url: SITE_URL + '/password-reset' callback_url: getSiteURL() + '/password-reset'
} }
}); });
} catch (err) { } catch (err) {
@@ -91,8 +91,8 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => {
payload: { payload: {
userId: user._id.toString() userId: user._id.toString()
}, },
expiresIn: JWT_SIGNUP_LIFETIME, expiresIn: getJwtSignupLifetime(),
secret: JWT_SIGNUP_SECRET secret: getJwtSignupSecret()
}); });
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
@@ -9,7 +9,7 @@ import {
import { pushKeys } from '../../helpers/key'; import { pushKeys } from '../../helpers/key';
import { eventPushSecrets } from '../../events'; import { eventPushSecrets } from '../../events';
import { EventService } from '../../services'; import { EventService } from '../../services';
import { postHogClient } from '../../services'; import { getPostHogClient } from '../../services';
interface PushSecret { interface PushSecret {
ciphertextKey: string; ciphertextKey: string;
@@ -38,6 +38,7 @@ export const pushSecrets = async (req: Request, res: Response) => {
// upload (encrypted) secrets to workspace with id [workspaceId] // upload (encrypted) secrets to workspace with id [workspaceId]
try { try {
const postHogClient = getPostHogClient();
let { secrets }: { secrets: PushSecret[] } = req.body; let { secrets }: { secrets: PushSecret[] } = req.body;
const { keys, environment, channel } = req.body; const { keys, environment, channel } = req.body;
const { workspaceId } = req.params; const { workspaceId } = req.params;
@@ -111,6 +112,7 @@ export const pullSecrets = async (req: Request, res: Response) => {
let secrets; let secrets;
let key; let key;
try { try {
const postHogClient = getPostHogClient();
const environment: string = req.query.environment as string; const environment: string = req.query.environment as string;
const channel: string = req.query.channel as string; const channel: string = req.query.channel as string;
const { workspaceId } = req.params; const { workspaceId } = req.params;
@@ -179,6 +181,7 @@ export const pullSecretsServiceToken = async (req: Request, res: Response) => {
let secrets; let secrets;
let key; let key;
try { try {
const postHogClient = getPostHogClient();
const environment: string = req.query.environment as string; const environment: string = req.query.environment as string;
const channel: string = req.query.channel as string; const channel: string = req.query.channel as string;
const { workspaceId } = req.params; const { workspaceId } = req.params;
@@ -1,7 +1,7 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { ServiceToken } from '../../models'; import { ServiceToken } from '../../models';
import { createToken } from '../../helpers/auth'; import { createToken } from '../../helpers/auth';
import { JWT_SERVICE_SECRET } from '../../config'; import { getJwtServiceSecret } from '../../config';
/** /**
* Return service token on request * Return service token on request
@@ -61,7 +61,7 @@ export const createServiceToken = async (req: Request, res: Response) => {
workspaceId workspaceId
}, },
expiresIn: expiresIn, expiresIn: expiresIn,
secret: JWT_SERVICE_SECRET secret: getJwtServiceSecret()
}); });
} catch (err) { } catch (err) {
return res.status(400).send({ return res.status(400).send({
@@ -1,13 +1,13 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { User } from '../../models'; import { User } from '../../models';
import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, INVITE_ONLY_SIGNUP } from '../../config';
import { import {
sendEmailVerification, sendEmailVerification,
checkEmailVerification, checkEmailVerification,
} from '../../helpers/signup'; } from '../../helpers/signup';
import { createToken } from '../../helpers/auth'; import { createToken } from '../../helpers/auth';
import { BadRequestError } from '../../utils/errors'; 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 * 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 { try {
email = req.body.email; email = req.body.email;
if (INVITE_ONLY_SIGNUP) { 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 // 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({}) const userCount = await User.countDocuments({})
if (userCount != 0) { if (userCount != 0) {
@@ -91,8 +91,8 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
payload: { payload: {
userId: user._id.toString() userId: user._id.toString()
}, },
expiresIn: JWT_SIGNUP_LIFETIME, expiresIn: getJwtSignupLifetime(),
secret: JWT_SIGNUP_SECRET secret: getJwtSignupSecret()
}); });
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
@@ -1,10 +1,7 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import Stripe from 'stripe'; import Stripe from 'stripe';
import { STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET } from '../../config'; import { getStripeSecretKey, getStripeWebhookSecret } from '../../config';
const stripe = new Stripe(STRIPE_SECRET_KEY, {
apiVersion: '2022-08-01'
});
/** /**
* Handle service provisioning/un-provisioning via Stripe * Handle service provisioning/un-provisioning via Stripe
@@ -16,11 +13,15 @@ export const handleWebhook = async (req: Request, res: Response) => {
let event; let event;
try { try {
// check request for valid stripe signature // check request for valid stripe signature
const stripe = new Stripe(getStripeSecretKey(), {
apiVersion: '2022-08-01'
});
const sig = req.headers['stripe-signature'] as string; const sig = req.headers['stripe-signature'] as string;
event = stripe.webhooks.constructEvent( event = stripe.webhooks.constructEvent(
req.body, req.body,
sig, sig,
STRIPE_WEBHOOK_SECRET // ? getStripeWebhookSecret()
); );
} catch (err) { } catch (err) {
Sentry.setUser({ email: req.user.email }); Sentry.setUser({ email: req.user.email });
@@ -1,13 +1,11 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { Request, Response } from 'express';
import crypto from 'crypto'; import crypto from 'crypto';
import bcrypt from 'bcrypt'; import bcrypt from 'bcrypt';
import { import {
APIKeyData APIKeyData
} from '../../models'; } from '../../models';
import { import { getSaltRounds } from '../../config';
SALT_ROUNDS
} from '../../config';
/** /**
* Return API key data for user with id [req.user_id] * 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 { name, expiresIn } = req.body;
const secret = crypto.randomBytes(16).toString('hex'); const secret = crypto.randomBytes(16).toString('hex');
const secretHash = await bcrypt.hash(secret, SALT_ROUNDS); const secretHash = await bcrypt.hash(secret, getSaltRounds());
const expiresAt = new Date(); const expiresAt = new Date();
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn); expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
+9 -12
View File
@@ -10,17 +10,17 @@ import { checkUserDevice } from '../../helpers/user';
import { sendMail } from '../../helpers/nodemailer'; import { sendMail } from '../../helpers/nodemailer';
import { TokenService } from '../../services'; import { TokenService } from '../../services';
import { EELogService } from '../../ee/services'; import { EELogService } from '../../ee/services';
import {
NODE_ENV,
JWT_MFA_LIFETIME,
JWT_MFA_SECRET
} from '../../config';
import { BadRequestError, InternalServerError } from '../../utils/errors'; import { BadRequestError, InternalServerError } from '../../utils/errors';
import { import {
TOKEN_EMAIL_MFA, TOKEN_EMAIL_MFA,
ACTION_LOGIN ACTION_LOGIN
} from '../../variables'; } from '../../variables';
import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this
import {
getNodeEnv,
getJwtMfaLifetime,
getJwtMfaSecret
} from '../../config';
declare module 'jsonwebtoken' { declare module 'jsonwebtoken' {
export interface UserIDJwtPayload extends jwt.JwtPayload { export interface UserIDJwtPayload extends jwt.JwtPayload {
@@ -28,8 +28,6 @@ declare module 'jsonwebtoken' {
} }
} }
const clientPublicKeys: any = {};
/** /**
* Log in user step 1: Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol * Log in user step 1: Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol
* @param req * @param req
@@ -126,8 +124,8 @@ export const login2 = async (req: Request, res: Response) => {
payload: { payload: {
userId: user._id.toString() userId: user._id.toString()
}, },
expiresIn: JWT_MFA_LIFETIME, expiresIn: getJwtMfaLifetime(),
secret: JWT_MFA_SECRET secret: getJwtMfaSecret()
}); });
const code = await TokenService.createToken({ const code = await TokenService.createToken({
@@ -165,7 +163,7 @@ export const login2 = async (req: Request, res: Response) => {
httpOnly: true, httpOnly: true,
path: '/', path: '/',
sameSite: 'strict', sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false secure: getNodeEnv() === 'production' ? true : false
}); });
// case: user does not have MFA enablgged // case: user does not have MFA enablgged
@@ -304,7 +302,7 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
httpOnly: true, httpOnly: true,
path: '/', path: '/',
sameSite: 'strict', sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false secure: getNodeEnv() === 'production' ? true : false
}); });
interface VerifyMfaTokenRes { interface VerifyMfaTokenRes {
@@ -348,4 +346,3 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
return res.status(200).send(resObj); return res.status(200).send(resObj);
} }
@@ -7,7 +7,7 @@ const { ValidationError } = mongoose.Error;
import { BadRequestError, InternalServerError, UnauthorizedRequestError, ValidationError as RouteValidationError } from '../../utils/errors'; import { BadRequestError, InternalServerError, UnauthorizedRequestError, ValidationError as RouteValidationError } from '../../utils/errors';
import { AnyBulkWriteOperation } from 'mongodb'; import { AnyBulkWriteOperation } from 'mongodb';
import { SECRET_PERSONAL, SECRET_SHARED } from "../../variables"; 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] * Create secret for workspace with id [workspaceId] and environment [environment]
@@ -15,6 +15,7 @@ import { postHogClient } from '../../services';
* @param res * @param res
*/ */
export const createSecret = async (req: Request, res: Response) => { export const createSecret = async (req: Request, res: Response) => {
const postHogClient = getPostHogClient();
const secretToCreate: CreateSecretRequestBody = req.body.secret; const secretToCreate: CreateSecretRequestBody = req.body.secret;
const { workspaceId, environment } = req.params const { workspaceId, environment } = req.params
const sanitizedSecret: SanitizedSecretForCreate = { const sanitizedSecret: SanitizedSecretForCreate = {
@@ -67,6 +68,7 @@ export const createSecret = async (req: Request, res: Response) => {
* @param res * @param res
*/ */
export const createSecrets = async (req: Request, res: Response) => { export const createSecrets = async (req: Request, res: Response) => {
const postHogClient = getPostHogClient();
const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets; const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets;
const { workspaceId, environment } = req.params const { workspaceId, environment } = req.params
const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = [] const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = []
@@ -128,6 +130,7 @@ export const createSecrets = async (req: Request, res: Response) => {
* @param res * @param res
*/ */
export const deleteSecrets = async (req: Request, res: Response) => { export const deleteSecrets = async (req: Request, res: Response) => {
const postHogClient = getPostHogClient();
const { workspaceId, environmentName } = req.params const { workspaceId, environmentName } = req.params
const secretIdsToDelete: string[] = req.body.secretIds const secretIdsToDelete: string[] = req.body.secretIds
@@ -181,6 +184,7 @@ export const deleteSecrets = async (req: Request, res: Response) => {
* @param res * @param res
*/ */
export const deleteSecret = async (req: Request, res: Response) => { export const deleteSecret = async (req: Request, res: Response) => {
const postHogClient = getPostHogClient();
await Secret.findByIdAndDelete(req._secret._id) await Secret.findByIdAndDelete(req._secret._id)
if (postHogClient) { if (postHogClient) {
@@ -209,6 +213,7 @@ export const deleteSecret = async (req: Request, res: Response) => {
* @returns * @returns
*/ */
export const updateSecrets = async (req: Request, res: Response) => { export const updateSecrets = async (req: Request, res: Response) => {
const postHogClient = getPostHogClient();
const { workspaceId, environmentName } = req.params const { workspaceId, environmentName } = req.params
const secretsModificationsRequested: ModifySecretRequestBody[] = req.body.secrets; const secretsModificationsRequested: ModifySecretRequestBody[] = req.body.secrets;
const [secretIdsUserCanModifyError, secretIdsUserCanModify] = await to(Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then()) 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 * @returns
*/ */
export const updateSecret = async (req: Request, res: Response) => { export const updateSecret = async (req: Request, res: Response) => {
const postHogClient = getPostHogClient();
const { workspaceId, environmentName } = req.params const { workspaceId, environmentName } = req.params
const secretModificationsRequested: ModifySecretRequestBody = req.body.secret; const secretModificationsRequested: ModifySecretRequestBody = req.body.secret;
@@ -329,6 +335,7 @@ export const updateSecret = async (req: Request, res: Response) => {
* @returns * @returns
*/ */
export const getSecrets = async (req: Request, res: Response) => { export const getSecrets = async (req: Request, res: Response) => {
const postHogClient = getPostHogClient();
const { environment } = req.query; const { environment } = req.query;
const { workspaceId } = req.params; const { workspaceId } = req.params;
@@ -15,7 +15,7 @@ import { UnauthorizedRequestError, ValidationError } from '../../utils/errors';
import { EventService } from '../../services'; import { EventService } from '../../services';
import { eventPushSecrets } from '../../events'; import { eventPushSecrets } from '../../events';
import { EESecretService, EELogService } from '../../ee/services'; import { EESecretService, EELogService } from '../../ee/services';
import { postHogClient } from '../../services'; import { getPostHogClient } from '../../services';
import { getChannelFromUserAgent } from '../../utils/posthog'; import { getChannelFromUserAgent } from '../../utils/posthog';
import { ABILITY_READ, ABILITY_WRITE } from '../../variables/organization'; import { ABILITY_READ, ABILITY_WRITE } from '../../variables/organization';
import { userHasNoAbility, userHasWorkspaceAccess, userHasWriteOnlyAbility } from '../../ee/helpers/checkMembershipPermissions'; import { userHasNoAbility, userHasWorkspaceAccess, userHasWriteOnlyAbility } from '../../ee/helpers/checkMembershipPermissions';
@@ -33,6 +33,8 @@ import {
*/ */
export const batchSecrets = async (req: Request, res: Response) => { export const batchSecrets = async (req: Request, res: Response) => {
const channel = getChannelFromUserAgent(req.headers['user-agent']); const channel = getChannelFromUserAgent(req.headers['user-agent']);
const postHogClient = getPostHogClient();
const { const {
workspaceId, workspaceId,
environment, environment,
@@ -326,6 +328,7 @@ export const createSecrets = async (req: Request, res: Response) => {
} }
} }
*/ */
const postHogClient = getPostHogClient();
const channel = getChannelFromUserAgent(req.headers['user-agent']) const channel = getChannelFromUserAgent(req.headers['user-agent'])
const { workspaceId, environment }: { workspaceId: string, environment: string } = req.body; 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 { workspaceId, environment, tagSlugs } = req.query;
const tagNamesList = typeof tagSlugs === 'string' && tagSlugs !== '' ? tagSlugs.split(',') : []; 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'; const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli';
// TODO: move type // 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 channel = getChannelFromUserAgent(req.headers['user-agent'])
const toDelete = req.secrets.map((s: any) => s._id); const toDelete = req.secrets.map((s: any) => s._id);
@@ -1,15 +1,13 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { Request, Response } from 'express';
import crypto from 'crypto'; import crypto from 'crypto';
import bcrypt from 'bcrypt'; import bcrypt from 'bcrypt';
import { import {
ServiceTokenData ServiceTokenData
} from '../../models'; } from '../../models';
import {
SALT_ROUNDS
} from '../../config';
import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissions'; import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissions';
import { ABILITY_READ } from '../../variables/organization'; import { ABILITY_READ } from '../../variables/organization';
import { getSaltRounds } from '../../config';
/** /**
* Return service token data associated with service token on request * Return service token data associated with service token on request
@@ -75,7 +73,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
} }
const secret = crypto.randomBytes(16).toString('hex'); const secret = crypto.randomBytes(16).toString('hex');
const secretHash = await bcrypt.hash(secret, SALT_ROUNDS); const secretHash = await bcrypt.hash(secret, getSaltRounds());
const expiresAt = new Date(); const expiresAt = new Date();
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn); expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
@@ -7,8 +7,8 @@ import {
} from '../../helpers/signup'; } from '../../helpers/signup';
import { issueAuthTokens } from '../../helpers/auth'; import { issueAuthTokens } from '../../helpers/auth';
import { INVITED, ACCEPTED } from '../../variables'; import { INVITED, ACCEPTED } from '../../variables';
import { NODE_ENV } from '../../config';
import request from '../../config/request'; import request from '../../config/request';
import { getNodeEnv, getLoopsApiKey } from '../../config';
/** /**
* Complete setting up user by adding their personal and auth information as part of the * 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; token = tokens.token;
// sending a welcome email to new users // 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", { await request.post("https://app.loops.so/api/v1/events/send", {
"email": email, "email": email,
"eventName": "Sign Up", "eventName": "Sign Up",
@@ -117,7 +117,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
}, { }, {
headers: { headers: {
"Accept": "application/json", "Accept": "application/json",
"Authorization": "Bearer " + process.env.LOOPS_API_KEY "Authorization": "Bearer " + getLoopsApiKey()
}, },
}); });
} }
@@ -127,7 +127,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
httpOnly: true, httpOnly: true,
path: '/', path: '/',
sameSite: 'strict', sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false secure: getNodeEnv() === 'production' ? true : false
}); });
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
@@ -232,7 +232,7 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
httpOnly: true, httpOnly: true,
path: '/', path: '/',
sameSite: 'strict', sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false secure: getNodeEnv() === 'production' ? true : false
}); });
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
@@ -19,7 +19,7 @@ import {
reformatPullSecrets reformatPullSecrets
} from '../../helpers/secret'; } from '../../helpers/secret';
import { pushKeys } from '../../helpers/key'; import { pushKeys } from '../../helpers/key';
import { postHogClient, EventService } from '../../services'; import { getPostHogClient, EventService } from '../../services';
import { eventPushSecrets } from '../../events'; import { eventPushSecrets } from '../../events';
interface V2PushSecret { interface V2PushSecret {
@@ -48,6 +48,7 @@ interface V2PushSecret {
export const pushWorkspaceSecrets = async (req: Request, res: Response) => { export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
// upload (encrypted) secrets to workspace with id [workspaceId] // upload (encrypted) secrets to workspace with id [workspaceId]
try { try {
const postHogClient = getPostHogClient();
let { secrets }: { secrets: V2PushSecret[] } = req.body; let { secrets }: { secrets: V2PushSecret[] } = req.body;
const { keys, environment, channel } = req.body; const { keys, environment, channel } = req.body;
const { workspaceId } = req.params; const { workspaceId } = req.params;
@@ -121,6 +122,7 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
export const pullSecrets = async (req: Request, res: Response) => { export const pullSecrets = async (req: Request, res: Response) => {
let secrets; let secrets;
try { try {
const postHogClient = getPostHogClient();
const environment: string = req.query.environment as string; const environment: string = req.query.environment as string;
const channel: string = req.query.channel as string; const channel: string = req.query.channel as string;
const { workspaceId } = req.params; const { workspaceId } = req.params;
@@ -1,10 +1,7 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { Request, Response } from 'express';
import Stripe from 'stripe'; import Stripe from 'stripe';
import { STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET } from '../../../config'; import { getStripeSecretKey, getStripeWebhookSecret } from '../../../config';
const stripe = new Stripe(STRIPE_SECRET_KEY, {
apiVersion: '2022-08-01'
});
/** /**
* Handle service provisioning/un-provisioning via Stripe * 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) => { export const handleWebhook = async (req: Request, res: Response) => {
let event; let event;
try { try {
const stripe = new Stripe(getStripeSecretKey(), {
apiVersion: '2022-08-01'
});
// check request for valid stripe signature // check request for valid stripe signature
const sig = req.headers['stripe-signature'] as string; const sig = req.headers['stripe-signature'] as string;
event = stripe.webhooks.constructEvent( event = stripe.webhooks.constructEvent(
req.body, req.body,
sig, sig,
STRIPE_WEBHOOK_SECRET // ? getStripeWebhookSecret()
); );
} catch (err) { } catch (err) {
Sentry.setUser({ email: req.user.email }); Sentry.setUser({ email: req.user.email });
+1 -3
View File
@@ -1,5 +1,3 @@
import { LICENSE_KEY } from '../../config';
/** /**
* Class to handle Enterprise Edition license actions * Class to handle Enterprise Edition license actions
*/ */
@@ -16,4 +14,4 @@ class EELicenseService {
} }
} }
export default new EELicenseService(LICENSE_KEY); export default new EELicenseService('N/A');
+12 -12
View File
@@ -1,5 +1,5 @@
import jwt from 'jsonwebtoken';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt'; import bcrypt from 'bcrypt';
import { import {
IUser, IUser,
@@ -7,12 +7,6 @@ import {
ServiceTokenData, ServiceTokenData,
APIKeyData APIKeyData
} from '../models'; } from '../models';
import {
JWT_AUTH_LIFETIME,
JWT_AUTH_SECRET,
JWT_REFRESH_LIFETIME,
JWT_REFRESH_SECRET
} from '../config';
import { import {
AccountNotFoundError, AccountNotFoundError,
ServiceTokenDataNotFoundError, ServiceTokenDataNotFoundError,
@@ -20,6 +14,12 @@ import {
UnauthorizedRequestError, UnauthorizedRequestError,
BadRequestError BadRequestError
} from '../utils/errors'; } from '../utils/errors';
import {
getJwtAuthLifetime,
getJwtAuthSecret,
getJwtRefreshLifetime,
getJwtRefreshSecret
} from '../config';
/** /**
* *
@@ -93,7 +93,7 @@ const getAuthUserPayload = async ({
let user; let user;
try { try {
const decodedToken = <jwt.UserIDJwtPayload>( const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(authTokenValue, JWT_AUTH_SECRET) jwt.verify(authTokenValue, getJwtAuthSecret())
); );
user = await User.findOne({ user = await User.findOne({
@@ -224,16 +224,16 @@ const issueAuthTokens = async ({ userId }: { userId: string }) => {
payload: { payload: {
userId userId
}, },
expiresIn: JWT_AUTH_LIFETIME, expiresIn: getJwtAuthLifetime(),
secret: JWT_AUTH_SECRET secret: getJwtAuthSecret()
}); });
refreshToken = createToken({ refreshToken = createToken({
payload: { payload: {
userId userId
}, },
expiresIn: JWT_REFRESH_LIFETIME, expiresIn: getJwtRefreshLifetime(),
secret: JWT_REFRESH_SECRET secret: getJwtRefreshSecret()
}); });
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
+3 -3
View File
@@ -12,8 +12,8 @@ import {
decryptSymmetric, decryptSymmetric,
decryptAsymmetric decryptAsymmetric
} from '../utils/crypto'; } from '../utils/crypto';
import { ENCRYPTION_KEY } from '../config';
import { SECRET_SHARED } from '../variables'; import { SECRET_SHARED } from '../variables';
import { getEncryptionKey } from '../config';
/** /**
* Create an inactive bot with name [name] for workspace with id [workspaceId] * Create an inactive bot with name [name] for workspace with id [workspaceId]
@@ -33,7 +33,7 @@ const createBot = async ({
const { publicKey, privateKey } = generateKeyPair(); const { publicKey, privateKey } = generateKeyPair();
const { ciphertext, iv, tag } = encryptSymmetric({ const { ciphertext, iv, tag } = encryptSymmetric({
plaintext: privateKey, plaintext: privateKey,
key: ENCRYPTION_KEY key: getEncryptionKey()
}); });
bot = await new Bot({ bot = await new Bot({
@@ -130,7 +130,7 @@ const getKey = async ({ workspaceId }: { workspaceId: string }) => {
ciphertext: bot.encryptedPrivateKey, ciphertext: bot.encryptedPrivateKey,
iv: bot.iv, iv: bot.iv,
tag: bot.tag, tag: bot.tag,
key: ENCRYPTION_KEY key: getEncryptionKey()
}); });
key = decryptAsymmetric({ key = decryptAsymmetric({
+18 -1
View File
@@ -29,6 +29,23 @@ const initDatabaseHelper = async ({
return mongoose.connection; 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 { export {
initDatabaseHelper initDatabaseHelper,
closeDatabaseHelper
} }
+3 -3
View File
@@ -1,9 +1,9 @@
import * as Sentry from '@sentry/node';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import handlebars from 'handlebars'; import handlebars from 'handlebars';
import nodemailer from 'nodemailer'; import nodemailer from 'nodemailer';
import { SMTP_FROM_NAME, SMTP_FROM_ADDRESS } from '../config'; import { getSmtpFromName, getSmtpFromAddress } from '../config';
import * as Sentry from '@sentry/node';
let smtpTransporter: nodemailer.Transporter; let smtpTransporter: nodemailer.Transporter;
@@ -34,7 +34,7 @@ const sendMail = async ({
const htmlToSend = temp(substitutions); const htmlToSend = temp(substitutions);
await smtpTransporter.sendMail({ await smtpTransporter.sendMail({
from: `"${SMTP_FROM_NAME}" <${SMTP_FROM_ADDRESS}>`, from: `"${getSmtpFromName()}" <${getSmtpFromAddress()}>`,
to: recipients.join(', '), to: recipients.join(', '),
subject: subjectLine, subject: subjectLine,
html: htmlToSend html: htmlToSend
+24 -16
View File
@@ -1,23 +1,14 @@
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import Stripe from 'stripe'; 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 { Types } from 'mongoose';
import { ACCEPTED } from '../variables'; import { ACCEPTED } from '../variables';
import { Organization, MembershipOrg } from '../models'; import { Organization, MembershipOrg } from '../models';
import {
const productToPriceMap = { getStripeSecretKey,
starter: STRIPE_PRODUCT_STARTER, getStripeProductPro,
team: STRIPE_PRODUCT_TEAM, getStripeProductTeam,
pro: STRIPE_PRODUCT_PRO getStripeProductStarter
}; } from '../config';
/** /**
* Create an organization with name [name] * Create an organization with name [name]
@@ -36,8 +27,11 @@ const createOrganization = async ({
let organization; let organization;
try { try {
// register stripe account // register stripe account
const stripe = new Stripe(getStripeSecretKey(), {
apiVersion: '2022-08-01'
});
if (STRIPE_SECRET_KEY) { if (getStripeSecretKey()) {
const customer = await stripe.customers.create({ const customer = await stripe.customers.create({
email, email,
description: name description: name
@@ -87,6 +81,16 @@ const initSubscriptionOrg = async ({
if (organization) { if (organization) {
if (organization.customerId) { if (organization.customerId) {
// initialize starter subscription with quantity of 0 // initialize starter subscription with quantity of 0
const stripe = new Stripe(getStripeSecretKey(), {
apiVersion: '2022-08-01'
});
const productToPriceMap = {
starter: getStripeProductStarter(),
team: getStripeProductTeam(),
pro: getStripeProductPro()
};
stripeSubscription = await stripe.subscriptions.create({ stripeSubscription = await stripe.subscriptions.create({
customer: organization.customerId, customer: organization.customerId,
items: [ items: [
@@ -139,6 +143,10 @@ const updateSubscriptionOrgQuantity = async ({
status: ACCEPTED status: ACCEPTED
}); });
const stripe = new Stripe(getStripeSecretKey(), {
apiVersion: '2022-08-01'
});
const subscription = ( const subscription = (
await stripe.subscriptions.list({ await stripe.subscriptions.list({
customer: organization.customerId customer: organization.customerId
+2 -4
View File
@@ -9,10 +9,8 @@ import {
TOKEN_EMAIL_ORG_INVITATION, TOKEN_EMAIL_ORG_INVITATION,
TOKEN_EMAIL_PASSWORD_RESET TOKEN_EMAIL_PASSWORD_RESET
} from '../variables'; } from '../variables';
import {
SALT_ROUNDS
} from '../config';
import { UnauthorizedRequestError } from '../utils/errors'; import { UnauthorizedRequestError } from '../utils/errors';
import { getSaltRounds } from '../config';
/** /**
* Create and store a token in the database for purpose [type] * Create and store a token in the database for purpose [type]
@@ -86,7 +84,7 @@ const createTokenHelper = async ({
const query: TokenDataQuery = { type }; const query: TokenDataQuery = { type };
const update: TokenDataUpdate = { const update: TokenDataUpdate = {
type, type,
tokenHash: await bcrypt.hash(token, SALT_ROUNDS), tokenHash: await bcrypt.hash(token, getSaltRounds()),
expiresAt expiresAt
} }
+167 -14
View File
@@ -1,28 +1,181 @@
import mongoose from 'mongoose';
import dotenv from 'dotenv'; import dotenv from 'dotenv';
dotenv.config(); 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 * as Sentry from '@sentry/node';
import { SENTRY_DSN, NODE_ENV, MONGO_URL } from './config';
import { server } from './app';
import { DatabaseService } from './services'; import { DatabaseService } from './services';
import { setUpHealthEndpoint } from './services/health'; import { setUpHealthEndpoint } from './services/health';
import { initSmtp } from './services/smtp'; import { initSmtp } from './services/smtp';
import { logTelemetryMessage } from './services';
import { setTransporter } from './helpers/nodemailer'; import { setTransporter } from './helpers/nodemailer';
import { createTestUserForDevelopment } from './utils/addDevelopmentUser'; 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';
import {
getMongoURL,
getNodeEnv,
getPort,
getSentryDSN,
getSiteURL
} from './config';
setUpHealthEndpoint(server); const main = async () => {
await infisical.connect({
token: process.env.INFISICAL_TOKEN!
});
setTransporter(initSmtp()); logTelemetryMessage();
setTransporter(initSmtp());
if (NODE_ENV !== 'test') { await DatabaseService.initDatabase(getMongoURL());
Sentry.init({ if (getNodeEnv() !== 'test') {
dsn: SENTRY_DSN, Sentry.init({
tracesSampleRate: 1.0, dsn: getSentryDSN(),
debug: NODE_ENV === 'production' ? false : true, tracesSampleRate: 1.0,
environment: NODE_ENV debug: getNodeEnv() === 'production' ? false : true,
}); environment: getNodeEnv()
});
}
patchRouterParam();
const app = express();
app.enable('trust proxy');
app.use(express.json());
app.use(cookieParser());
app.use(
cors({
credentials: true,
origin: getSiteURL()
})
);
app.use(requestIp.mw());
if (getNodeEnv() === '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(getPort(), () => {
getLogger("backend-main").info(`Server started listening at port ${getPort()}`)
});
createTestUserForDevelopment();
setUpHealthEndpoint(server);
server.on('close', async () => {
await DatabaseService.closeDatabase();
})
return server;
} }
createTestUserForDevelopment() export default main();
+29 -29
View File
@@ -1,5 +1,5 @@
import request from '../config/request';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import request from '../config/request';
import { import {
INTEGRATION_AZURE_KEY_VAULT, INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_HEROKU, INTEGRATION_HEROKU,
@@ -15,18 +15,18 @@ import {
INTEGRATION_GITLAB_TOKEN_URL INTEGRATION_GITLAB_TOKEN_URL
} from '../variables'; } from '../variables';
import { import {
SITE_URL, getSiteURL,
CLIENT_ID_AZURE, getClientIdAzure,
CLIENT_ID_VERCEL, getClientSecretAzure,
CLIENT_ID_NETLIFY, getClientSecretHeroku,
CLIENT_ID_GITHUB, getClientIdVercel,
CLIENT_ID_GITLAB, getClientSecretVercel,
CLIENT_SECRET_AZURE, getClientIdNetlify,
CLIENT_SECRET_HEROKU, getClientSecretNetlify,
CLIENT_SECRET_VERCEL, getClientIdGitHub,
CLIENT_SECRET_NETLIFY, getClientSecretGitHub,
CLIENT_SECRET_GITHUB, getClientIdGitLab,
CLIENT_SECRET_GITLAB, getClientSecretGitLab
} from '../config'; } from '../config';
interface ExchangeCodeAzureResponse { interface ExchangeCodeAzureResponse {
@@ -159,9 +159,9 @@ const exchangeCodeAzure = async ({
grant_type: 'authorization_code', grant_type: 'authorization_code',
code: code, code: code,
scope: 'https://vault.azure.net/.default openid offline_access', scope: 'https://vault.azure.net/.default openid offline_access',
client_id: CLIENT_ID_AZURE, client_id: getClientIdAzure(),
client_secret: CLIENT_SECRET_AZURE, client_secret: getClientSecretAzure(),
redirect_uri: `${SITE_URL}/integrations/azure-key-vault/oauth2/callback` redirect_uri: `${getSiteURL()}/integrations/azure-key-vault/oauth2/callback`
} as any) } as any)
)).data; )).data;
@@ -204,7 +204,7 @@ const exchangeCodeHeroku = async ({
new URLSearchParams({ new URLSearchParams({
grant_type: 'authorization_code', grant_type: 'authorization_code',
code: code, code: code,
client_secret: CLIENT_SECRET_HEROKU client_secret: getClientSecretHeroku()
} as any) } as any)
)).data; )).data;
@@ -242,9 +242,9 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => {
INTEGRATION_VERCEL_TOKEN_URL, INTEGRATION_VERCEL_TOKEN_URL,
new URLSearchParams({ new URLSearchParams({
code: code, code: code,
client_id: CLIENT_ID_VERCEL, client_id: getClientIdVercel(),
client_secret: CLIENT_SECRET_VERCEL, client_secret: getClientSecretVercel(),
redirect_uri: `${SITE_URL}/integrations/vercel/oauth2/callback` redirect_uri: `${getSiteURL()}/integrations/vercel/oauth2/callback`
} as any) } as any)
) )
).data; ).data;
@@ -282,9 +282,9 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => {
new URLSearchParams({ new URLSearchParams({
grant_type: 'authorization_code', grant_type: 'authorization_code',
code: code, code: code,
client_id: CLIENT_ID_NETLIFY, client_id: getClientIdNetlify(),
client_secret: CLIENT_SECRET_NETLIFY, client_secret: getClientSecretNetlify(),
redirect_uri: `${SITE_URL}/integrations/netlify/oauth2/callback` redirect_uri: `${getSiteURL()}/integrations/netlify/oauth2/callback`
} as any) } as any)
) )
).data; ).data;
@@ -333,10 +333,10 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => {
res = ( res = (
await request.get(INTEGRATION_GITHUB_TOKEN_URL, { await request.get(INTEGRATION_GITHUB_TOKEN_URL, {
params: { params: {
client_id: CLIENT_ID_GITHUB, client_id: getClientIdGitHub(),
client_secret: CLIENT_SECRET_GITHUB, client_secret: getClientSecretGitHub(),
code: code, code: code,
redirect_uri: `${SITE_URL}/integrations/github/oauth2/callback` redirect_uri: `${getSiteURL()}/integrations/github/oauth2/callback`
}, },
headers: { headers: {
'Accept': 'application/json', 'Accept': 'application/json',
@@ -379,9 +379,9 @@ const exchangeCodeGitlab = async ({ code }: { code: string }) => {
new URLSearchParams({ new URLSearchParams({
grant_type: 'authorization_code', grant_type: 'authorization_code',
code: code, code: code,
client_id: CLIENT_ID_GITLAB, client_id: getClientIdGitLab(),
client_secret: CLIENT_SECRET_GITLAB, client_secret: getClientSecretGitLab(),
redirect_uri: `${SITE_URL}/integrations/gitlab/oauth2/callback` redirect_uri: `${getSiteURL()}/integrations/gitlab/oauth2/callback`
} as any), } as any),
{ {
headers: { headers: {
+15 -15
View File
@@ -1,5 +1,5 @@
import request from '../config/request';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import request from '../config/request';
import { import {
IIntegrationAuth IIntegrationAuth
} from '../models'; } from '../models';
@@ -8,14 +8,6 @@ import {
INTEGRATION_HEROKU, INTEGRATION_HEROKU,
INTEGRATION_GITLAB, INTEGRATION_GITLAB,
} from '../variables'; } from '../variables';
import {
SITE_URL,
CLIENT_ID_AZURE,
CLIENT_ID_GITLAB,
CLIENT_SECRET_AZURE,
CLIENT_SECRET_HEROKU,
CLIENT_SECRET_GITLAB
} from '../config';
import { import {
INTEGRATION_AZURE_TOKEN_URL, INTEGRATION_AZURE_TOKEN_URL,
INTEGRATION_HEROKU_TOKEN_URL, INTEGRATION_HEROKU_TOKEN_URL,
@@ -24,6 +16,14 @@ import {
import { import {
IntegrationService IntegrationService
} from '../services'; } from '../services';
import {
getSiteURL,
getClientIdAzure,
getClientSecretAzure,
getClientSecretHeroku,
getClientIdGitLab,
getClientSecretGitLab
} from '../config';
interface RefreshTokenAzureResponse { interface RefreshTokenAzureResponse {
token_type: string; token_type: string;
@@ -133,11 +133,11 @@ const exchangeRefreshAzure = async ({
const { data }: { data: RefreshTokenAzureResponse } = await request.post( const { data }: { data: RefreshTokenAzureResponse } = await request.post(
INTEGRATION_AZURE_TOKEN_URL, INTEGRATION_AZURE_TOKEN_URL,
new URLSearchParams({ new URLSearchParams({
client_id: CLIENT_ID_AZURE, client_id: getClientIdAzure(),
scope: 'openid offline_access', scope: 'openid offline_access',
refresh_token: refreshToken, refresh_token: refreshToken,
grant_type: 'refresh_token', grant_type: 'refresh_token',
client_secret: CLIENT_SECRET_AZURE client_secret: getClientSecretAzure()
} as any) } as any)
); );
@@ -180,7 +180,7 @@ const exchangeRefreshHeroku = async ({
new URLSearchParams({ new URLSearchParams({
grant_type: 'refresh_token', grant_type: 'refresh_token',
refresh_token: refreshToken, refresh_token: refreshToken,
client_secret: CLIENT_SECRET_HEROKU client_secret: getClientSecretHeroku()
} as any) } as any)
); );
@@ -223,9 +223,9 @@ const exchangeRefreshGitLab = async ({
new URLSearchParams({ new URLSearchParams({
grant_type: 'refresh_token', grant_type: 'refresh_token',
refresh_token: refreshToken, refresh_token: refreshToken,
client_id: CLIENT_ID_GITLAB, client_id: getClientIdGitLab,
client_secret: CLIENT_SECRET_GITLAB, client_secret: getClientSecretGitLab(),
redirect_uri: `${SITE_URL}/integrations/gitlab/oauth2/callback` redirect_uri: `${getSiteURL()}/integrations/gitlab/oauth2/callback`
} as any), } as any),
{ {
headers: { headers: {
@@ -1,16 +1,13 @@
import { ErrorRequestHandler } from "express";
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { InternalServerError, UnauthorizedRequestError } from "../utils/errors"; import { ErrorRequestHandler } from "express";
import { InternalServerError } from "../utils/errors";
import { getLogger } from "../utils/logger"; import { getLogger } from "../utils/logger";
import RequestError, { LogLevel } from "../utils/requestError"; import RequestError, { LogLevel } from "../utils/requestError";
import { NODE_ENV } from "../config"; import { getNodeEnv } from '../config';
import { TokenExpiredError } from 'jsonwebtoken';
export const requestErrorHandler: ErrorRequestHandler = (error: RequestError | Error, req, res, next) => { export const requestErrorHandler: ErrorRequestHandler = (error: RequestError | Error, req, res, next) => {
if (res.headersSent) return next(); if (res.headersSent) return next();
if (NODE_ENV !== "production") { if (getNodeEnv() !== "production") {
/* eslint-disable no-console */ /* eslint-disable no-console */
console.log(error) console.log(error)
/* eslint-enable no-console */ /* eslint-enable no-console */
+2 -2
View File
@@ -1,8 +1,8 @@
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import { User } from '../models'; import { User } from '../models';
import { JWT_MFA_SECRET } from '../config';
import { BadRequestError, UnauthorizedRequestError } from '../utils/errors'; import { BadRequestError, UnauthorizedRequestError } from '../utils/errors';
import { getJwtMfaSecret } from '../config';
declare module 'jsonwebtoken' { declare module 'jsonwebtoken' {
export interface UserIDJwtPayload extends jwt.JwtPayload { 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'})) if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'}))
const decodedToken = <jwt.UserIDJwtPayload>( const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(AUTH_TOKEN_VALUE, JWT_MFA_SECRET) jwt.verify(AUTH_TOKEN_VALUE, getJwtMfaSecret())
); );
const user = await User.findOne({ const user = await User.findOne({
@@ -1,8 +1,8 @@
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import { ServiceToken } from '../models'; import { ServiceToken } from '../models';
import { JWT_SERVICE_SECRET } from '../config';
import { BadRequestError, UnauthorizedRequestError } from '../utils/errors'; import { BadRequestError, UnauthorizedRequestError } from '../utils/errors';
import { getJwtServiceSecret } from '../config';
// TODO: deprecate // TODO: deprecate
declare module 'jsonwebtoken' { 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'})) if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'}))
const decodedToken = <jwt.UserIDJwtPayload>( const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(AUTH_TOKEN_VALUE, JWT_SERVICE_SECRET) jwt.verify(AUTH_TOKEN_VALUE, getJwtServiceSecret())
); );
const serviceToken = await ServiceToken.findOne({ const serviceToken = await ServiceToken.findOne({
+2 -2
View File
@@ -1,8 +1,8 @@
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import { User } from '../models'; import { User } from '../models';
import { JWT_SIGNUP_SECRET } from '../config';
import { BadRequestError, UnauthorizedRequestError } from '../utils/errors'; import { BadRequestError, UnauthorizedRequestError } from '../utils/errors';
import { getJwtSignupSecret } from '../config';
declare module 'jsonwebtoken' { declare module 'jsonwebtoken' {
export interface UserIDJwtPayload extends jwt.JwtPayload { 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'})) if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'}))
const decodedToken = <jwt.UserIDJwtPayload>( const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(AUTH_TOKEN_VALUE, JWT_SIGNUP_SECRET) jwt.verify(AUTH_TOKEN_VALUE, getJwtSignupSecret())
); );
const user = await User.findOne({ const user = await User.findOne({
-1
View File
@@ -1,5 +1,4 @@
import { Schema, model } from 'mongoose'; import { Schema, model } from 'mongoose';
import { EMAIL_TOKEN_LIFETIME } from '../config';
export interface IToken { export interface IToken {
email: string; email: string;
+17 -1
View File
@@ -1,16 +1,32 @@
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import { getLogger } from '../utils/logger'; import { getLogger } from '../utils/logger';
import { initDatabaseHelper } from '../helpers/database'; import {
initDatabaseHelper,
closeDatabaseHelper
} from '../helpers/database';
/** /**
* Class to handle database actions * Class to handle database actions
*/ */
class DatabaseService { class DatabaseService {
/**
* Initialize database connection
* @param {Object} obj
* @param {String} obj.mongoURL - mongo connection string
* @returns
*/
static async initDatabase(MONGO_URL: string) { static async initDatabase(MONGO_URL: string) {
return await initDatabaseHelper({ return await initDatabaseHelper({
mongoURL: MONGO_URL mongoURL: MONGO_URL
}); });
} }
/**
* Close database conection
*/
static async closeDatabase() {
return await closeDatabaseHelper();
}
} }
export default DatabaseService; export default DatabaseService;
+37 -20
View File
@@ -1,27 +1,44 @@
import { PostHog } from 'posthog-node'; import { PostHog } from 'posthog-node';
import {
NODE_ENV,
POSTHOG_HOST,
POSTHOG_PROJECT_API_KEY,
TELEMETRY_ENABLED
} from '../config';
import { getLogger } from '../utils/logger'; import { getLogger } from '../utils/logger';
import {
getNodeEnv,
getTelemetryEnabled,
getPostHogProjectApiKey,
getPostHogHost
} from '../config';
if(!TELEMETRY_ENABLED){ /**
getLogger("backend-main").info([ * Logs telemetry enable/disable notice.
"", */
"To improve, Infisical collects telemetry data about general usage.", const logTelemetryMessage = () => {
"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.", if(!getTelemetryEnabled()){
"To opt into telemetry, you can set `TELEMETRY_ENABLED=true` within the environment variables.", getLogger("backend-main").info([
].join('\n')) "",
"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) { * Return an instance of the PostHog client initialized.
// case: enable opt-out telemetry in production * @returns
postHogClient = new PostHog(POSTHOG_PROJECT_API_KEY, { */
host: POSTHOG_HOST const getPostHogClient = () => {
}); let postHogClient: any;
if (getNodeEnv() === 'production' && getTelemetryEnabled()) {
// case: enable opt-out telemetry in production
postHogClient = new PostHog(getPostHogProjectApiKey(), {
host: getPostHogHost()
});
}
return postHogClient;
}
export {
logTelemetryMessage,
getPostHogClient
} }
export default postHogClient;
+3 -2
View File
@@ -1,13 +1,14 @@
import DatabaseService from './DatabaseService'; import DatabaseService from './DatabaseService';
import postHogClient from './PostHogClient'; import { logTelemetryMessage, getPostHogClient } from './PostHogClient';
import BotService from './BotService'; import BotService from './BotService';
import EventService from './EventService'; import EventService from './EventService';
import IntegrationService from './IntegrationService'; import IntegrationService from './IntegrationService';
import TokenService from './TokenService'; import TokenService from './TokenService';
export { export {
logTelemetryMessage,
getPostHogClient,
DatabaseService, DatabaseService,
postHogClient,
BotService, BotService,
EventService, EventService,
IntegrationService, IntegrationService,
+49 -49
View File
@@ -1,11 +1,4 @@
import nodemailer from 'nodemailer'; import nodemailer from 'nodemailer';
import {
SMTP_HOST,
SMTP_PORT,
SMTP_USERNAME,
SMTP_PASSWORD,
SMTP_SECURE
} from '../config';
import { import {
SMTP_HOST_SENDGRID, SMTP_HOST_SENDGRID,
SMTP_HOST_MAILGUN, SMTP_HOST_MAILGUN,
@@ -14,55 +7,62 @@ import {
} from '../variables'; } from '../variables';
import SMTPConnection from 'nodemailer/lib/smtp-connection'; import SMTPConnection from 'nodemailer/lib/smtp-connection';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import {
getSmtpHost,
getSmtpUsername,
getSmtpPassword,
getSmtpSecure,
getSmtpPort
} from '../config';
const mailOpts: SMTPConnection.Options = { export const initSmtp = () => {
host: SMTP_HOST, const mailOpts: SMTPConnection.Options = {
port: SMTP_PORT as number host: getSmtpHost(),
}; port: getSmtpPort()
if (SMTP_USERNAME && SMTP_PASSWORD) {
mailOpts.auth = {
user: SMTP_USERNAME,
pass: SMTP_PASSWORD
}; };
}
if (SMTP_SECURE) { if (getSmtpUsername() && getSmtpPassword()) {
switch (SMTP_HOST) { mailOpts.auth = {
case SMTP_HOST_SENDGRID: user: getSmtpUsername(),
mailOpts.requireTLS = true; pass: getSmtpPassword()
break; };
case SMTP_HOST_MAILGUN: }
mailOpts.requireTLS = true;
mailOpts.tls = { if (getSmtpSecure() ? getSmtpSecure() : false) {
ciphers: 'TLSv1.2' switch (getSmtpHost()) {
} case SMTP_HOST_SENDGRID:
break; mailOpts.requireTLS = true;
case SMTP_HOST_SOCKETLABS: break;
mailOpts.requireTLS = true; case SMTP_HOST_MAILGUN:
mailOpts.tls = { mailOpts.requireTLS = true;
ciphers: 'TLSv1.2'
}
break;
case SMTP_HOST_ZOHOMAIL:
mailOpts.requireTLS = true;
mailOpts.tls = {
ciphers: 'TLSv1.2'
}
break;
default:
if (SMTP_HOST.includes('amazonaws.com')) {
mailOpts.tls = { mailOpts.tls = {
ciphers: 'TLSv1.2' ciphers: 'TLSv1.2'
} }
} else { break;
mailOpts.secure = true; case SMTP_HOST_SOCKETLABS:
} mailOpts.requireTLS = true;
break; mailOpts.tls = {
ciphers: 'TLSv1.2'
}
break;
case SMTP_HOST_ZOHOMAIL:
mailOpts.requireTLS = true;
mailOpts.tls = {
ciphers: 'TLSv1.2'
}
break;
default:
if (getSmtpHost().includes('amazonaws.com')) {
mailOpts.tls = {
ciphers: 'TLSv1.2'
}
} else {
mailOpts.secure = true;
}
break;
}
} }
}
export const initSmtp = () => {
const transporter = nodemailer.createTransport(mailOpts); const transporter = nodemailer.createTransport(mailOpts);
transporter transporter
.verify() .verify()
@@ -73,7 +73,7 @@ export const initSmtp = () => {
.catch((err) => { .catch((err) => {
Sentry.setUser(null); Sentry.setUser(null);
Sentry.captureException( Sentry.captureException(
`SMTP - Failed to connect to ${SMTP_HOST}:${SMTP_PORT} \n\t${err}` `SMTP - Failed to connect to ${getSmtpHost()}:${getSmtpPort()} \n\t${err}`
); );
}); });
+2 -2
View File
@@ -4,12 +4,12 @@
* *
************************************************************************************************/ ************************************************************************************************/
import { NODE_ENV } from "../config"
import { Key, Membership, MembershipOrg, Organization, User, Workspace } from "../models"; import { Key, Membership, MembershipOrg, Organization, User, Workspace } from "../models";
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { getNodeEnv } from '../config';
export const createTestUserForDevelopment = async () => { export const createTestUserForDevelopment = async () => {
if (NODE_ENV === "development") { if (getNodeEnv() === "development") {
const testUserEmail = "[email protected]" const testUserEmail = "[email protected]"
const testUserPassword = "testInfisical1" const testUserPassword = "testInfisical1"
const testUserId = "63cefa6ec8d3175601cfa980" const testUserId = "63cefa6ec8d3175601cfa980"
+8 -4
View File
@@ -1,7 +1,7 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
import { createLogger, format, transports } from 'winston'; import { createLogger, format, transports } from 'winston';
import LokiTransport from 'winston-loki'; import LokiTransport from 'winston-loki';
import { LOKI_HOST, NODE_ENV } from '../config'; import { getLokiHost, getNodeEnv } from '../config';
const { combine, colorize, label, printf, splat, timestamp } = format; const { combine, colorize, label, printf, splat, timestamp } = format;
@@ -25,10 +25,10 @@ const createLoggerWithLabel = (level: string, label: string) => {
}) })
] ]
//* Add LokiTransport if it's enabled //* Add LokiTransport if it's enabled
if(LOKI_HOST !== undefined){ if(getLokiHost() !== undefined){
_transports.push( _transports.push(
new LokiTransport({ new LokiTransport({
host: LOKI_HOST, host: getLokiHost(),
handleExceptions: true, handleExceptions: true,
handleRejections: true, handleRejections: true,
batching: true, batching: true,
@@ -37,7 +37,11 @@ const createLoggerWithLabel = (level: string, label: string) => {
format: format.combine( format: format.combine(
format.json() 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: getNodeEnv()
},
onConnectionError: (err: Error)=> console.error('Connection error while connecting to Loki Server.\n', err) onConnectionError: (err: Error)=> console.error('Connection error while connecting to Loki Server.\n', err)
}) })
) )
+2 -2
View File
@@ -1,5 +1,5 @@
import { Request } from 'express' import { Request } from 'express'
import { VERBOSE_ERROR_OUTPUT } from '../config' import { getVerboseErrorOutput } from '../config';
export enum LogLevel { export enum LogLevel {
DEBUG = 100, DEBUG = 100,
@@ -87,7 +87,7 @@ export default class RequestError extends Error{
}, this.context) }, this.context)
//* Omit sensitive information from context that can leak internal workings of this program if user is not developer //* Omit sensitive information from context that can leak internal workings of this program if user is not developer
if(!VERBOSE_ERROR_OUTPUT){ if(!getVerboseErrorOutput()){
_context = this._omit(_context, [ _context = this._omit(_context, [
'stacktrace', 'stacktrace',
'exception', 'exception',
+2 -2
View File
@@ -34,7 +34,7 @@ import {
INTEGRATION_FLYIO_API_URL, INTEGRATION_FLYIO_API_URL,
INTEGRATION_CIRCLECI_API_URL, INTEGRATION_CIRCLECI_API_URL,
INTEGRATION_TRAVISCI_API_URL, INTEGRATION_TRAVISCI_API_URL,
INTEGRATION_OPTIONS, getIntegrationOptions
} from "./integration"; } from "./integration";
import { OWNER, ADMIN, MEMBER, INVITED, ACCEPTED } from "./organization"; import { OWNER, ADMIN, MEMBER, INVITED, ACCEPTED } from "./organization";
import { SECRET_SHARED, SECRET_PERSONAL } from "./secret"; import { SECRET_SHARED, SECRET_PERSONAL } from "./secret";
@@ -113,7 +113,7 @@ export {
ACTION_UPDATE_SECRETS, ACTION_UPDATE_SECRETS,
ACTION_DELETE_SECRETS, ACTION_DELETE_SECRETS,
ACTION_READ_SECRETS, ACTION_READ_SECRETS,
INTEGRATION_OPTIONS, getIntegrationOptions,
SMTP_HOST_SENDGRID, SMTP_HOST_SENDGRID,
SMTP_HOST_MAILGUN, SMTP_HOST_MAILGUN,
SMTP_HOST_SOCKETLABS, SMTP_HOST_SOCKETLABS,
+156 -155
View File
@@ -1,13 +1,11 @@
import { import {
CLIENT_ID_AZURE, getClientIdHeroku,
CLIENT_ID_GITLAB getClientSlugVercel,
getClientIdNetlify,
getClientIdAzure,
getClientIdGitLab,
getClientIdGitHub
} from '../config'; } from '../config';
import {
CLIENT_ID_HEROKU,
CLIENT_ID_NETLIFY,
CLIENT_ID_GITHUB,
CLIENT_SLUG_VERCEL
} from "../config";
// integrations // integrations
const INTEGRATION_AZURE_KEY_VAULT = 'azure-key-vault'; const INTEGRATION_AZURE_KEY_VAULT = 'azure-key-vault';
@@ -48,7 +46,6 @@ const INTEGRATION_GITHUB_TOKEN_URL =
"https://github.com/login/oauth/access_token"; "https://github.com/login/oauth/access_token";
const INTEGRATION_GITLAB_TOKEN_URL = "https://gitlab.com/oauth/token"; const INTEGRATION_GITLAB_TOKEN_URL = "https://gitlab.com/oauth/token";
// integration apps endpoints // integration apps endpoints
const INTEGRATION_HEROKU_API_URL = "https://api.heroku.com"; const INTEGRATION_HEROKU_API_URL = "https://api.heroku.com";
const INTEGRATION_GITLAB_API_URL = "https://gitlab.com/api"; const INTEGRATION_GITLAB_API_URL = "https://gitlab.com/api";
@@ -59,156 +56,160 @@ const INTEGRATION_FLYIO_API_URL = "https://api.fly.io/graphql";
const INTEGRATION_CIRCLECI_API_URL = "https://circleci.com/api"; const INTEGRATION_CIRCLECI_API_URL = "https://circleci.com/api";
const INTEGRATION_TRAVISCI_API_URL = "https://api.travis-ci.com"; const INTEGRATION_TRAVISCI_API_URL = "https://api.travis-ci.com";
// TODO: deprecate types? const getIntegrationOptions = () => {
const INTEGRATION_OPTIONS = [ const INTEGRATION_OPTIONS = [
{ {
name: 'Heroku', name: 'Heroku',
slug: 'heroku', slug: 'heroku',
image: 'Heroku.png', image: 'Heroku.png',
isAvailable: true, isAvailable: true,
type: 'oauth', type: 'oauth',
clientId: CLIENT_ID_HEROKU, clientId: getClientIdHeroku(),
docsLink: '' docsLink: ''
}, },
{ {
name: 'Vercel', name: 'Vercel',
slug: 'vercel', slug: 'vercel',
image: 'Vercel.png', image: 'Vercel.png',
isAvailable: true, isAvailable: true,
type: 'oauth', type: 'oauth',
clientId: '', clientId: '',
clientSlug: CLIENT_SLUG_VERCEL, clientSlug: getClientSlugVercel(),
docsLink: '' docsLink: ''
}, },
{ {
name: 'Netlify', name: 'Netlify',
slug: 'netlify', slug: 'netlify',
image: 'Netlify.png', image: 'Netlify.png',
isAvailable: true, isAvailable: true,
type: 'oauth', type: 'oauth',
clientId: CLIENT_ID_NETLIFY, clientId: getClientIdNetlify(),
docsLink: '' docsLink: ''
}, },
{ {
name: 'GitHub', name: 'GitHub',
slug: 'github', slug: 'github',
image: 'GitHub.png', image: 'GitHub.png',
isAvailable: true, isAvailable: true,
type: 'oauth', type: 'oauth',
clientId: CLIENT_ID_GITHUB, clientId: getClientIdGitHub(),
docsLink: '' docsLink: ''
}, },
{ {
name: 'Render', name: 'Render',
slug: 'render', slug: 'render',
image: 'Render.png', image: 'Render.png',
isAvailable: true, isAvailable: true,
type: 'pat', type: 'pat',
clientId: '', clientId: '',
docsLink: '' docsLink: ''
}, },
{ {
name: 'Fly.io', name: 'Fly.io',
slug: 'flyio', slug: 'flyio',
image: 'Flyio.svg', image: 'Flyio.svg',
isAvailable: true, isAvailable: true,
type: 'pat', type: 'pat',
clientId: '', clientId: '',
docsLink: '' docsLink: ''
}, },
{ {
name: 'AWS Parameter Store', name: 'AWS Parameter Store',
slug: 'aws-parameter-store', slug: 'aws-parameter-store',
image: 'Amazon Web Services.png', image: 'Amazon Web Services.png',
isAvailable: true, isAvailable: true,
type: 'custom', type: 'custom',
clientId: '', clientId: '',
docsLink: '' docsLink: ''
}, },
{ {
name: 'AWS Secret Manager', name: 'AWS Secret Manager',
slug: 'aws-secret-manager', slug: 'aws-secret-manager',
image: 'Amazon Web Services.png', image: 'Amazon Web Services.png',
isAvailable: true, isAvailable: true,
type: 'custom', type: 'custom',
clientId: '', clientId: '',
docsLink: '' docsLink: ''
}, },
{ {
name: 'Azure Key Vault', name: 'Azure Key Vault',
slug: 'azure-key-vault', slug: 'azure-key-vault',
image: 'Microsoft Azure.png', image: 'Microsoft Azure.png',
isAvailable: true, isAvailable: true,
type: 'oauth', type: 'oauth',
clientId: CLIENT_ID_AZURE, clientId: getClientIdAzure(),
docsLink: '' docsLink: ''
}, },
{ {
name: 'Circle CI', name: 'Circle CI',
slug: 'circleci', slug: 'circleci',
image: 'Circle CI.png', image: 'Circle CI.png',
isAvailable: true, isAvailable: true,
type: 'pat', type: 'pat',
clientId: '', clientId: '',
docsLink: '' docsLink: ''
}, },
{ {
name: 'GitLab', name: 'GitLab',
slug: 'gitlab', slug: 'gitlab',
image: 'GitLab.png', image: 'GitLab.png',
isAvailable: true, isAvailable: true,
type: 'custom', type: 'custom',
clientId: CLIENT_ID_GITLAB, clientId: getClientIdGitLab(),
docsLink: '' docsLink: ''
}, },
{ {
name: 'Travis CI', name: 'Travis CI',
slug: 'travisci', slug: 'travisci',
image: 'Travis CI.png', image: 'Travis CI.png',
isAvailable: true, isAvailable: true,
type: 'pat', type: 'pat',
clientId: '', clientId: '',
docsLink: '' docsLink: ''
}, },
{ {
name: 'Google Cloud Platform', name: 'Google Cloud Platform',
slug: 'gcp', slug: 'gcp',
image: 'Google Cloud Platform.png', image: 'Google Cloud Platform.png',
isAvailable: false, isAvailable: false,
type: '', type: '',
clientId: '', clientId: '',
docsLink: '' docsLink: ''
} }
] ]
return INTEGRATION_OPTIONS;
}
export { export {
INTEGRATION_AZURE_KEY_VAULT, INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_AWS_PARAMETER_STORE, INTEGRATION_AWS_PARAMETER_STORE,
INTEGRATION_AWS_SECRET_MANAGER, INTEGRATION_AWS_SECRET_MANAGER,
INTEGRATION_HEROKU, INTEGRATION_HEROKU,
INTEGRATION_VERCEL, INTEGRATION_VERCEL,
INTEGRATION_NETLIFY, INTEGRATION_NETLIFY,
INTEGRATION_GITHUB, INTEGRATION_GITHUB,
INTEGRATION_GITLAB, INTEGRATION_GITLAB,
INTEGRATION_RENDER, INTEGRATION_RENDER,
INTEGRATION_FLYIO, INTEGRATION_FLYIO,
INTEGRATION_CIRCLECI, INTEGRATION_CIRCLECI,
INTEGRATION_TRAVISCI, INTEGRATION_TRAVISCI,
INTEGRATION_SET, INTEGRATION_SET,
INTEGRATION_OAUTH2, INTEGRATION_OAUTH2,
INTEGRATION_AZURE_TOKEN_URL, INTEGRATION_AZURE_TOKEN_URL,
INTEGRATION_HEROKU_TOKEN_URL, INTEGRATION_HEROKU_TOKEN_URL,
INTEGRATION_VERCEL_TOKEN_URL, INTEGRATION_VERCEL_TOKEN_URL,
INTEGRATION_NETLIFY_TOKEN_URL, INTEGRATION_NETLIFY_TOKEN_URL,
INTEGRATION_GITHUB_TOKEN_URL, INTEGRATION_GITHUB_TOKEN_URL,
INTEGRATION_GITLAB_API_URL, INTEGRATION_GITLAB_API_URL,
INTEGRATION_HEROKU_API_URL, INTEGRATION_HEROKU_API_URL,
INTEGRATION_GITLAB_TOKEN_URL, INTEGRATION_GITLAB_TOKEN_URL,
INTEGRATION_VERCEL_API_URL, INTEGRATION_VERCEL_API_URL,
INTEGRATION_NETLIFY_API_URL, INTEGRATION_NETLIFY_API_URL,
INTEGRATION_RENDER_API_URL, INTEGRATION_RENDER_API_URL,
INTEGRATION_FLYIO_API_URL, INTEGRATION_FLYIO_API_URL,
INTEGRATION_CIRCLECI_API_URL, INTEGRATION_CIRCLECI_API_URL,
INTEGRATION_TRAVISCI_API_URL, INTEGRATION_TRAVISCI_API_URL,
INTEGRATION_OPTIONS, getIntegrationOptions
}; };
+9 -4
View File
@@ -50,9 +50,9 @@
"url": "self-hosting" "url": "self-hosting"
}, },
{ {
"name": "SDK", "name": "SDKs",
"icon": "puzzle-piece", "icon": "puzzle-piece",
"url": "sdk" "url": "sdks"
}, },
{ {
"name": "API Reference", "name": "API Reference",
@@ -192,9 +192,14 @@
] ]
}, },
{ {
"group": "SDK", "group": "SDKs",
"pages": [ "pages": [
"sdk/overview/usage" "sdks/overview/node",
"sdks/overview/python",
"sdks/overview/java",
"sdks/overview/ruby",
"sdks/overview/go",
"sdks/overview/rust"
] ]
}, },
{ {
-104
View File
@@ -1,104 +0,0 @@
---
title: "Usage"
---
<Note>
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.
</Note>
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.
<Tabs>
<Tab title="Javascript">
## 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` |
| `defaultValues` | Default values for secrets if they aren't fetched/passed in | `{}` |
## Access a Secret Value
```js
const dbURL = infisical.getSecretValue("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.getSecret("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}`);
});
```
</Tab>
<Tab title="Python">
Coming soon.
</Tab>
</Tabs>
+8
View File
@@ -0,0 +1,8 @@
---
title: "Go"
---
Coming soon.
Follow this GitHub
[issue](https://github.com/Infisical/infisical/issues/436) to stay updated.
+8
View File
@@ -0,0 +1,8 @@
---
title: "Java"
---
Coming soon.
Follow this GitHub
[issue](https://github.com/Infisical/infisical/issues/434) to stay updated.
+154
View File
@@ -0,0 +1,154 @@
---
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-node` to your project.
```bash
npm install infisical-node --save
```
## Initialization
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).
<ResponseField name="options" type="object">
<Expandable title="properties">
<ResponseField name="token" type="string">
An [Infisical Token](/getting-started/dashboard/token) scoped to a project
and environment
</ResponseField>
<ResponseField
name="siteURL"
type="string"
default="https://app.infisical.com"
>
Your self-hosted absolute site URL including the protocol (e.g.
`https://app.infisical.com`)
</ResponseField>
<ResponseField name="debug" type="boolean" default="false">
Whether or not debug mode is on
</ResponseField>
<ResponseField name="attachToProcessEnv" type="boolean" default="false">
Whether or not to attach fetched secrets to `process.env`
</ResponseField>
</Expandable>
</ResponseField>
### 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.
<ResponseField name="options" type="object">
<Expandable title="properties">
<ResponseField name="token" type="string">
An [Infisical Token](/getting-started/dashboard/token) scoped to a project
and environment
</ResponseField>
<ResponseField
name="siteURL"
type="string"
default="https://app.infisical.com"
>
Your self-hosted absolute site URL including the protocol (e.g.
`https://app.infisical.com`)
</ResponseField>
<ResponseField name="debug" type="boolean" default="false">
Whether or not debug mode is on
</ResponseField>
</Expandable>
</ResponseField>
<Tabs>
<Tab title="ES6">
```js
import infisical from "infisical-node";
const main = async () => {
await infisical.connect({
token: "your_infisical_token",
});
// your app logic
}
main();
```
</Tab>
<Tab title="ES5">
```js
const infisical = require("infisical-node");
infisical.connect({
token: "your_infisical_token"
})
.then(() => {
// your application logic
})
.catch(err => {
console.error('Error: ', err);
})
````
</Tab>
</Tabs>
## Usage
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.
<ResponseField name="key" type="string" required>
The key of the secret
</ResponseField>
```js
const value = infisical.get("SOME_KEY");
```
## 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}`);
});
};
```
<Warning>
We do not recommend hardcoding your [Infisical
Token](/getting-started/dashboard/token). Setting it as an environment
variable would be best.
</Warning>
+8
View File
@@ -0,0 +1,8 @@
---
title: "Python"
---
Coming soon.
Follow this GitHub
[issue](https://github.com/Infisical/infisical/issues/433) to stay updated.
+8
View File
@@ -0,0 +1,8 @@
---
title: "Ruby"
---
Coming soon.
Follow this GitHub
[issue](https://github.com/Infisical/infisical/issues/435) to stay updated.
+8
View File
@@ -0,0 +1,8 @@
---
title: "Rust"
---
Coming soon.
Follow this GitHub
[issue](https://github.com/Infisical/infisical/issues/437) to stay updated.
@@ -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_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_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` | | `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_URL` | ❗️ MongoDB instance connection string either to container instance or MongoDB Cloud | `None` |
| `MONGO_USERNAME` | MongoDB username if using container | `None` | | `MONGO_USERNAME` | MongoDB username if using container | `None` |
| `MONGO_PASSWORD` | MongoDB password if using container | `None` | | `MONGO_PASSWORD` | MongoDB password if using container | `None` |
+52 -11
View File
@@ -1,5 +1,5 @@
{ {
"name": "npm-proj-1677883018530-0.7603125731052582NtcmfK", "name": "frontend",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
@@ -46,6 +46,7 @@
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"http-proxy": "^1.18.1", "http-proxy": "^1.18.1",
"i18next": "^22.4.9", "i18next": "^22.4.9",
"infisical-node": "^1.0.37",
"jspdf": "^2.5.1", "jspdf": "^2.5.1",
"jsrp": "^0.2.4", "jsrp": "^0.2.4",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",
@@ -13367,6 +13368,26 @@
"node": ">=8" "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": { "node_modules/inflight": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -17363,8 +17384,7 @@
"node_modules/proxy-from-env": { "node_modules/proxy-from-env": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
"dev": true
}, },
"node_modules/pump": { "node_modules/pump": {
"version": "3.0.0", "version": "3.0.0",
@@ -21761,9 +21781,9 @@
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
}, },
"node_modules/webpack": { "node_modules/webpack": {
"version": "5.75.0", "version": "5.76.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz",
"integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/eslint-scope": "^3.7.3", "@types/eslint-scope": "^3.7.3",
@@ -32078,6 +32098,28 @@
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
"dev": true "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": { "inflight": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -34869,8 +34911,7 @@
"proxy-from-env": { "proxy-from-env": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
"dev": true
}, },
"pump": { "pump": {
"version": "3.0.0", "version": "3.0.0",
@@ -38113,9 +38154,9 @@
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
}, },
"webpack": { "webpack": {
"version": "5.75.0", "version": "5.76.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz",
"integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/eslint-scope": "^3.7.3", "@types/eslint-scope": "^3.7.3",
+2 -1
View File
@@ -39,8 +39,8 @@
"@reduxjs/toolkit": "^1.8.3", "@reduxjs/toolkit": "^1.8.3",
"@stripe/react-stripe-js": "^1.16.3", "@stripe/react-stripe-js": "^1.16.3",
"@stripe/stripe-js": "^1.46.0", "@stripe/stripe-js": "^1.46.0",
"@types/argon2-browser": "^1.18.1",
"@tanstack/react-query": "^4.23.0", "@tanstack/react-query": "^4.23.0",
"@types/argon2-browser": "^1.18.1",
"add": "^2.0.6", "add": "^2.0.6",
"argon2-browser": "^1.18.0", "argon2-browser": "^1.18.0",
"axios": "^0.27.2", "axios": "^0.27.2",
@@ -53,6 +53,7 @@
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"http-proxy": "^1.18.1", "http-proxy": "^1.18.1",
"i18next": "^22.4.9", "i18next": "^22.4.9",
"infisical-node": "^1.0.37",
"jspdf": "^2.5.1", "jspdf": "^2.5.1",
"jsrp": "^0.2.4", "jsrp": "^0.2.4",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",