1
0
mirror of https://github.com/Infisical/infisical.git synced 2025-03-22 11:45:48 +00:00

Add eslint rule and fix as many issues as possible

This commit is contained in:
Khoa Le
2023-06-19 23:32:35 -04:00
parent e9eacc445d
commit 9d33e4756b
689 changed files with 9446 additions and 21821 deletions
.vscode
backend
.eslintrc.prettierrcenvironment.d.tsjest.config.tspackage-lock.jsonpackage.json
src
config
controllers
ee
events
helpers
index.ts
integrations
interfaces
middleware
serviceAccounts/dto
utils
middleware
models
routes
services
types
utils
validation
variables
swagger
tests
helper
integration-tests/routes/v2
setupTests.ts
unit-tests/utils
frontend
.eslintrc.js.prettierrcpackage.json
src
components
RouteGuard.tsx
analytics
basic
billing
context/Notifications
dashboard
integrations
login
navigation
signup
utilities
v2
config
const.ts
context
ee
helpers
hooks
i18n.ts
layouts
pages
404.tsx_app.tsx
activity
api
apiKey
auth
bot
environments
files
integrations
organization
serviceToken
user
userActions
workspace
dashboard.tsx
dashboard
email-not-verified.tsx
home
integrations
login.tsx
login/provider
noprojects.tsxpassword-reset.tsxrequestnewinvite.tsxsaml-sso.tsx
settings
billing
org/[id]
personal
project
signup.tsxsignupinvite.tsx
users
verify-email.tsx
reactQuery.ts
services
views
DashboardPage
Settings
CreateServiceAccountPage
OrgSettingsPage
PersonalSettingsPage/SecuritySection
ProjectSettingsPage
package.json

3
.vscode/settings.json vendored Normal file

@ -0,0 +1,3 @@
{
"workbench.editor.wrapTabs": true
}

@ -1,12 +1,21 @@
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"plugins": ["@typescript-eslint", "unused-imports"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"no-console": 2
"no-console": 2,
"quotes": ["error", "double", { "avoidEscape": true }],
"comma-dangle": ["error", "only-multiline"],
"@typescript-eslint/no-unused-vars": "off",
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"warn",
{ "vars": "all", "varsIgnorePattern": "^_", "args": "after-used", "argsIgnorePattern": "^_" }
],
"sort-imports": ["error", { "ignoreDeclarationSort": true }]
}
}

7
backend/.prettierrc Normal file

@ -0,0 +1,7 @@
{
"singleQuote": false,
"printWidth": 100,
"trailingComma": "none",
"tabWidth": 2,
"semi": true
}

@ -14,7 +14,7 @@ declare global {
JWT_SIGNUP_LIFETIME: string;
JWT_SIGNUP_SECRET: string;
MONGO_URL: string;
NODE_ENV: 'development' | 'staging' | 'testing' | 'production';
NODE_ENV: "development" | "staging" | "testing" | "production";
VERBOSE_ERROR_OUTPUT: string;
LOKI_HOST: string;
CLIENT_ID_HEROKU: string;

@ -1,9 +1,9 @@
export default {
preset: 'ts-jest',
testEnvironment: 'node',
collectCoverageFrom: ['src/*.{js,ts}', '!**/node_modules/**'],
modulePaths: ['<rootDir>/src'],
testMatch: ['<rootDir>/tests/**/*.test.ts'],
setupFiles: ['<rootDir>/test-resources/env-vars.js'],
setupFilesAfterEnv: ['<rootDir>/tests/setupTests.ts']
preset: "ts-jest",
testEnvironment: "node",
collectCoverageFrom: ["src/*.{js,ts}", "!**/node_modules/**"],
modulePaths: ["<rootDir>/src"],
testMatch: ["<rootDir>/tests/**/*.test.ts"],
setupFiles: ["<rootDir>/test-resources/env-vars.js"],
setupFilesAfterEnv: ["<rootDir>/tests/setupTests.ts"],
};

12521
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -99,6 +99,7 @@
"@typescript-eslint/parser": "^5.40.1",
"cross-env": "^7.0.3",
"eslint": "^8.26.0",
"eslint-plugin-unused-imports": "^2.0.0",
"install": "^0.13.0",
"jest": "^29.3.1",
"jest-junit": "^15.0.0",

@ -1,93 +1,93 @@
import InfisicalClient from 'infisical-node';
import InfisicalClient from "infisical-node";
export const client = new InfisicalClient({
token: process.env.INFISICAL_TOKEN!
token: process.env.INFISICAL_TOKEN!,
});
export const getPort = async () => (await client.getSecret('PORT')).secretValue || 4000;
export const getPort = async () => (await client.getSecret("PORT")).secretValue || 4000;
export const getEncryptionKey = async () => {
const secretValue = (await client.getSecret('ENCRYPTION_KEY')).secretValue;
return secretValue === '' ? undefined : secretValue;
const secretValue = (await client.getSecret("ENCRYPTION_KEY")).secretValue;
return secretValue === "" ? undefined : secretValue;
}
export const getRootEncryptionKey = async () => {
const secretValue = (await client.getSecret('ROOT_ENCRYPTION_KEY')).secretValue;
return secretValue === '' ? undefined : secretValue;
const secretValue = (await client.getSecret("ROOT_ENCRYPTION_KEY")).secretValue;
return secretValue === "" ? undefined : secretValue;
}
export const getInviteOnlySignup = async () => (await client.getSecret('INVITE_ONLY_SIGNUP')).secretValue === 'true'
export const getSaltRounds = async () => parseInt((await client.getSecret('SALT_ROUNDS')).secretValue) || 10;
export const getJwtAuthLifetime = async () => (await client.getSecret('JWT_AUTH_LIFETIME')).secretValue || '10d';
export const getJwtAuthSecret = async () => (await client.getSecret('JWT_AUTH_SECRET')).secretValue;
export const getJwtMfaLifetime = async () => (await client.getSecret('JWT_MFA_LIFETIME')).secretValue || '5m';
export const getJwtMfaSecret = async () => (await client.getSecret('JWT_MFA_LIFETIME')).secretValue || '5m';
export const getJwtRefreshLifetime = async () => (await client.getSecret('JWT_REFRESH_LIFETIME')).secretValue || '90d';
export const getJwtRefreshSecret = async () => (await client.getSecret('JWT_REFRESH_SECRET')).secretValue;
export const getJwtServiceSecret = async () => (await client.getSecret('JWT_SERVICE_SECRET')).secretValue;
export const getJwtSignupLifetime = async () => (await client.getSecret('JWT_SIGNUP_LIFETIME')).secretValue || '15m';
export const getJwtProviderAuthSecret = async () => (await client.getSecret('JWT_PROVIDER_AUTH_SECRET')).secretValue;
export const getJwtProviderAuthLifetime = async () => (await client.getSecret('JWT_PROVIDER_AUTH_LIFETIME')).secretValue || '15m';
export const getJwtSignupSecret = async () => (await client.getSecret('JWT_SIGNUP_SECRET')).secretValue;
export const getMongoURL = async () => (await client.getSecret('MONGO_URL')).secretValue;
export const getNodeEnv = async () => (await client.getSecret('NODE_ENV')).secretValue || 'production';
export const getVerboseErrorOutput = async () => (await client.getSecret('VERBOSE_ERROR_OUTPUT')).secretValue === 'true' && true;
export const getLokiHost = async () => (await client.getSecret('LOKI_HOST')).secretValue;
export const getClientIdAzure = async () => (await client.getSecret('CLIENT_ID_AZURE')).secretValue;
export const getClientIdHeroku = async () => (await client.getSecret('CLIENT_ID_HEROKU')).secretValue;
export const getClientIdVercel = async () => (await client.getSecret('CLIENT_ID_VERCEL')).secretValue;
export const getClientIdNetlify = async () => (await client.getSecret('CLIENT_ID_NETLIFY')).secretValue;
export const getClientIdGitHub = async () => (await client.getSecret('CLIENT_ID_GITHUB')).secretValue;
export const getClientIdGitLab = async () => (await client.getSecret('CLIENT_ID_GITLAB')).secretValue;
export const getClientIdGoogle = async () => (await client.getSecret('CLIENT_ID_GOOGLE')).secretValue;
export const getClientSecretAzure = async () => (await client.getSecret('CLIENT_SECRET_AZURE')).secretValue;
export const getClientSecretHeroku = async () => (await client.getSecret('CLIENT_SECRET_HEROKU')).secretValue;
export const getClientSecretVercel = async () => (await client.getSecret('CLIENT_SECRET_VERCEL')).secretValue;
export const getClientSecretNetlify = async () => (await client.getSecret('CLIENT_SECRET_NETLIFY')).secretValue;
export const getClientSecretGitHub = async () => (await client.getSecret('CLIENT_SECRET_GITHUB')).secretValue;
export const getClientSecretGitLab = async () => (await client.getSecret('CLIENT_SECRET_GITLAB')).secretValue;
export const getClientSecretGoogle = async () => (await client.getSecret('CLIENT_SECRET_GOOGLE')).secretValue;
export const getClientSlugVercel = async () => (await client.getSecret('CLIENT_SLUG_VERCEL')).secretValue;
export const getPostHogHost = async () => (await client.getSecret('POSTHOG_HOST')).secretValue || 'https://app.posthog.com';
export const getPostHogProjectApiKey = async () => (await client.getSecret('POSTHOG_PROJECT_API_KEY')).secretValue || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE';
export const getSentryDSN = async () => (await client.getSecret('SENTRY_DSN')).secretValue;
export const getSiteURL = async () => (await client.getSecret('SITE_URL')).secretValue;
export const getSmtpHost = async () => (await client.getSecret('SMTP_HOST')).secretValue;
export const getSmtpSecure = async () => (await client.getSecret('SMTP_SECURE')).secretValue === 'true' || false;
export const getSmtpPort = async () => parseInt((await client.getSecret('SMTP_PORT')).secretValue) || 587;
export const getSmtpUsername = async () => (await client.getSecret('SMTP_USERNAME')).secretValue;
export const getSmtpPassword = async () => (await client.getSecret('SMTP_PASSWORD')).secretValue;
export const getSmtpFromAddress = async () => (await client.getSecret('SMTP_FROM_ADDRESS')).secretValue;
export const getSmtpFromName = async () => (await client.getSecret('SMTP_FROM_NAME')).secretValue || 'Infisical';
export const getInviteOnlySignup = async () => (await client.getSecret("INVITE_ONLY_SIGNUP")).secretValue === "true"
export const getSaltRounds = async () => parseInt((await client.getSecret("SALT_ROUNDS")).secretValue) || 10;
export const getJwtAuthLifetime = async () => (await client.getSecret("JWT_AUTH_LIFETIME")).secretValue || "10d";
export const getJwtAuthSecret = async () => (await client.getSecret("JWT_AUTH_SECRET")).secretValue;
export const getJwtMfaLifetime = async () => (await client.getSecret("JWT_MFA_LIFETIME")).secretValue || "5m";
export const getJwtMfaSecret = async () => (await client.getSecret("JWT_MFA_LIFETIME")).secretValue || "5m";
export const getJwtRefreshLifetime = async () => (await client.getSecret("JWT_REFRESH_LIFETIME")).secretValue || "90d";
export const getJwtRefreshSecret = async () => (await client.getSecret("JWT_REFRESH_SECRET")).secretValue;
export const getJwtServiceSecret = async () => (await client.getSecret("JWT_SERVICE_SECRET")).secretValue;
export const getJwtSignupLifetime = async () => (await client.getSecret("JWT_SIGNUP_LIFETIME")).secretValue || "15m";
export const getJwtProviderAuthSecret = async () => (await client.getSecret("JWT_PROVIDER_AUTH_SECRET")).secretValue;
export const getJwtProviderAuthLifetime = async () => (await client.getSecret("JWT_PROVIDER_AUTH_LIFETIME")).secretValue || "15m";
export const getJwtSignupSecret = async () => (await client.getSecret("JWT_SIGNUP_SECRET")).secretValue;
export const getMongoURL = async () => (await client.getSecret("MONGO_URL")).secretValue;
export const getNodeEnv = async () => (await client.getSecret("NODE_ENV")).secretValue || "production";
export const getVerboseErrorOutput = async () => (await client.getSecret("VERBOSE_ERROR_OUTPUT")).secretValue === "true" && true;
export const getLokiHost = async () => (await client.getSecret("LOKI_HOST")).secretValue;
export const getClientIdAzure = async () => (await client.getSecret("CLIENT_ID_AZURE")).secretValue;
export const getClientIdHeroku = async () => (await client.getSecret("CLIENT_ID_HEROKU")).secretValue;
export const getClientIdVercel = async () => (await client.getSecret("CLIENT_ID_VERCEL")).secretValue;
export const getClientIdNetlify = async () => (await client.getSecret("CLIENT_ID_NETLIFY")).secretValue;
export const getClientIdGitHub = async () => (await client.getSecret("CLIENT_ID_GITHUB")).secretValue;
export const getClientIdGitLab = async () => (await client.getSecret("CLIENT_ID_GITLAB")).secretValue;
export const getClientIdGoogle = async () => (await client.getSecret("CLIENT_ID_GOOGLE")).secretValue;
export const getClientSecretAzure = async () => (await client.getSecret("CLIENT_SECRET_AZURE")).secretValue;
export const getClientSecretHeroku = async () => (await client.getSecret("CLIENT_SECRET_HEROKU")).secretValue;
export const getClientSecretVercel = async () => (await client.getSecret("CLIENT_SECRET_VERCEL")).secretValue;
export const getClientSecretNetlify = async () => (await client.getSecret("CLIENT_SECRET_NETLIFY")).secretValue;
export const getClientSecretGitHub = async () => (await client.getSecret("CLIENT_SECRET_GITHUB")).secretValue;
export const getClientSecretGitLab = async () => (await client.getSecret("CLIENT_SECRET_GITLAB")).secretValue;
export const getClientSecretGoogle = async () => (await client.getSecret("CLIENT_SECRET_GOOGLE")).secretValue;
export const getClientSlugVercel = async () => (await client.getSecret("CLIENT_SLUG_VERCEL")).secretValue;
export const getPostHogHost = async () => (await client.getSecret("POSTHOG_HOST")).secretValue || "https://app.posthog.com";
export const getPostHogProjectApiKey = async () => (await client.getSecret("POSTHOG_PROJECT_API_KEY")).secretValue || "phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE";
export const getSentryDSN = async () => (await client.getSecret("SENTRY_DSN")).secretValue;
export const getSiteURL = async () => (await client.getSecret("SITE_URL")).secretValue;
export const getSmtpHost = async () => (await client.getSecret("SMTP_HOST")).secretValue;
export const getSmtpSecure = async () => (await client.getSecret("SMTP_SECURE")).secretValue === "true" || false;
export const getSmtpPort = async () => parseInt((await client.getSecret("SMTP_PORT")).secretValue) || 587;
export const getSmtpUsername = async () => (await client.getSecret("SMTP_USERNAME")).secretValue;
export const getSmtpPassword = async () => (await client.getSecret("SMTP_PASSWORD")).secretValue;
export const getSmtpFromAddress = async () => (await client.getSecret("SMTP_FROM_ADDRESS")).secretValue;
export const getSmtpFromName = async () => (await client.getSecret("SMTP_FROM_NAME")).secretValue || "Infisical";
export const getLicenseKey = async () => {
const secretValue = (await client.getSecret('LICENSE_KEY')).secretValue;
return secretValue === '' ? undefined : secretValue;
const secretValue = (await client.getSecret("LICENSE_KEY")).secretValue;
return secretValue === "" ? undefined : secretValue;
}
export const getLicenseServerKey = async () => {
const secretValue = (await client.getSecret('LICENSE_SERVER_KEY')).secretValue;
return secretValue === '' ? undefined : secretValue;
const secretValue = (await client.getSecret("LICENSE_SERVER_KEY")).secretValue;
return secretValue === "" ? undefined : secretValue;
}
export const getLicenseServerUrl = async () => (await client.getSecret('LICENSE_SERVER_URL')).secretValue || 'https://portal.infisical.com';
export const getLicenseServerUrl = async () => (await client.getSecret("LICENSE_SERVER_URL")).secretValue || "https://portal.infisical.com";
// TODO: deprecate from here
export const getStripeProductStarter = async () => (await client.getSecret('STRIPE_PRODUCT_STARTER')).secretValue;
export const getStripeProductPro = async () => (await client.getSecret('STRIPE_PRODUCT_PRO')).secretValue;
export const getStripeProductTeam = async () => (await client.getSecret('STRIPE_PRODUCT_TEAM')).secretValue;
export const getStripePublishableKey = async () => (await client.getSecret('STRIPE_PUBLISHABLE_KEY')).secretValue;
export const getStripeSecretKey = async () => (await client.getSecret('STRIPE_SECRET_KEY')).secretValue;
export const getStripeWebhookSecret = async () => (await client.getSecret('STRIPE_WEBHOOK_SECRET')).secretValue;
export const getStripeProductStarter = async () => (await client.getSecret("STRIPE_PRODUCT_STARTER")).secretValue;
export const getStripeProductPro = async () => (await client.getSecret("STRIPE_PRODUCT_PRO")).secretValue;
export const getStripeProductTeam = async () => (await client.getSecret("STRIPE_PRODUCT_TEAM")).secretValue;
export const getStripePublishableKey = async () => (await client.getSecret("STRIPE_PUBLISHABLE_KEY")).secretValue;
export const getStripeSecretKey = async () => (await client.getSecret("STRIPE_SECRET_KEY")).secretValue;
export const getStripeWebhookSecret = async () => (await client.getSecret("STRIPE_WEBHOOK_SECRET")).secretValue;
export const getTelemetryEnabled = async () => (await client.getSecret('TELEMETRY_ENABLED')).secretValue !== 'false' && true;
export const getLoopsApiKey = async () => (await client.getSecret('LOOPS_API_KEY')).secretValue;
export const getSmtpConfigured = async () => (await client.getSecret('SMTP_HOST')).secretValue == '' || (await client.getSecret('SMTP_HOST')).secretValue == undefined ? false : true
export const getTelemetryEnabled = async () => (await client.getSecret("TELEMETRY_ENABLED")).secretValue !== "false" && true;
export const getLoopsApiKey = async () => (await client.getSecret("LOOPS_API_KEY")).secretValue;
export const getSmtpConfigured = async () => (await client.getSecret("SMTP_HOST")).secretValue == "" || (await client.getSecret("SMTP_HOST")).secretValue == undefined ? false : true
export const getHttpsEnabled = async () => {
if ((await getNodeEnv()) != "production") {
// no https for anything other than prod
return false
}
if ((await client.getSecret('HTTPS_ENABLED')).secretValue == undefined || (await client.getSecret('HTTPS_ENABLED')).secretValue == "") {
if ((await client.getSecret("HTTPS_ENABLED")).secretValue == undefined || (await client.getSecret("HTTPS_ENABLED")).secretValue == "") {
// default when no value present
return true
}
return (await client.getSecret('HTTPS_ENABLED')).secretValue === 'true' && true
return (await client.getSecret("HTTPS_ENABLED")).secretValue === "true" && true
}

@ -1,16 +1,16 @@
import axios from 'axios';
import axiosRetry from 'axios-retry';
import axios from "axios";
import axiosRetry from "axios-retry";
import {
getLicenseServerKeyAuthToken,
setLicenseServerKeyAuthToken,
getLicenseKeyAuthToken,
setLicenseKeyAuthToken
} from './storage';
getLicenseServerKeyAuthToken,
setLicenseKeyAuthToken,
setLicenseServerKeyAuthToken,
} from "./storage";
import {
getLicenseKey,
getLicenseServerKey,
getLicenseServerUrl
} from './index';
getLicenseServerUrl,
} from "./index";
// should have JWT to interact with the license server
export const licenseServerKeyRequest = axios.create();
@ -35,8 +35,8 @@ export const refreshLicenseServerKeyToken = async () => {
`${licenseServerUrl}/api/auth/v1/license-server-login`, {},
{
headers: {
'X-API-KEY': licenseServerKey
}
"X-API-KEY": licenseServerKey,
},
}
);
@ -53,8 +53,8 @@ export const refreshLicenseKeyToken = async () => {
`${licenseServerUrl}/api/auth/v1/license-login`, {},
{
headers: {
'X-API-KEY': licenseKey
}
"X-API-KEY": licenseKey,
},
}
);
@ -86,7 +86,7 @@ licenseServerKeyRequest.interceptors.response.use((response) => {
// refresh
const token = await refreshLicenseServerKeyToken();
axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
axios.defaults.headers.common["Authorization"] = "Bearer " + token;
return licenseServerKeyRequest(originalRequest);
}
@ -116,7 +116,7 @@ licenseKeyRequest.interceptors.response.use((response) => {
// refresh
const token = await refreshLicenseKeyToken();
axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
axios.defaults.headers.common["Authorization"] = "Bearer " + token;
return licenseKeyRequest(originalRequest);
}

@ -5,7 +5,7 @@ const MemoryLicenseServerKeyTokenStorage = () => {
setToken: (token: string) => {
authToken = token;
},
getToken: () => authToken
getToken: () => authToken,
};
};
@ -16,7 +16,7 @@ const MemoryLicenseKeyTokenStorage = () => {
setToken: (token: string) => {
authToken = token;
},
getToken: () => authToken
getToken: () => authToken,
};
};

@ -1,36 +1,36 @@
import { Request, Response } from 'express';
import fs from 'fs';
import path from 'path';
import jwt from 'jsonwebtoken';
import * as bigintConversion from 'bigint-conversion';
import { Request, Response } from "express";
import fs from "fs";
import path from "path";
import jwt from "jsonwebtoken";
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,
TokenVersion
} from '../../models';
import { createToken, issueAuthTokens, clearTokens } from '../../helpers/auth';
import { checkUserDevice } from '../../helpers/user';
LoginSRPDetail,
TokenVersion,
User,
} from "../../models";
import { clearTokens, createToken, issueAuthTokens } from "../../helpers/auth";
import { checkUserDevice } from "../../helpers/user";
import {
ACTION_LOGIN,
ACTION_LOGOUT,
AUTH_MODE_JWT
} from '../../variables';
AUTH_MODE_JWT,
} from "../../variables";
import {
BadRequestError,
UnauthorizedRequestError
} from '../../utils/errors';
import { EELogService } from '../../ee/services';
import { getChannelFromUserAgent } from '../../utils/posthog';
UnauthorizedRequestError,
} from "../../utils/errors";
import { EELogService } from "../../ee/services";
import { getChannelFromUserAgent } from "../../utils/posthog";
import {
getJwtRefreshSecret,
getHttpsEnabled,
getJwtAuthLifetime,
getJwtAuthSecret,
getHttpsEnabled
} from '../../config';
getJwtRefreshSecret,
} from "../../config";
declare module 'jsonwebtoken' {
declare module "jsonwebtoken" {
export interface UserIDJwtPayload extends jwt.JwtPayload {
userId: string;
refreshVersion?: number;
@ -46,20 +46,20 @@ declare module 'jsonwebtoken' {
export const login1 = async (req: Request, res: Response) => {
const {
email,
clientPublicKey
clientPublicKey,
}: { email: string; clientPublicKey: string } = req.body;
const user = await User.findOne({
email
}).select('+salt +verifier');
email,
}).select("+salt +verifier");
if (!user) throw new Error('Failed to find user');
if (!user) throw new Error("Failed to find user");
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier
verifier: user.verifier,
},
async () => {
// generate server-side public key
@ -73,7 +73,7 @@ export const login1 = async (req: Request, res: Response) => {
return res.status(200).send({
serverPublicKey,
salt: user.salt
salt: user.salt,
});
}
);
@ -89,10 +89,10 @@ export const login1 = async (req: Request, res: Response) => {
export const login2 = async (req: Request, res: Response) => {
const { email, clientProof } = req.body;
const user = await User.findOne({
email
}).select('+salt +verifier +publicKey +encryptedPrivateKey +iv +tag');
email,
}).select("+salt +verifier +publicKey +encryptedPrivateKey +iv +tag");
if (!user) throw new Error('Failed to find user');
if (!user) throw new Error("Failed to find user");
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: email })
@ -105,7 +105,7 @@ export const login2 = async (req: Request, res: Response) => {
{
salt: user.salt,
verifier: user.verifier,
b: loginSRPDetailFromDB.serverBInt
b: loginSRPDetailFromDB.serverBInt,
},
async () => {
server.setClientPublicKey(loginSRPDetailFromDB.clientPublicKey);
@ -117,33 +117,33 @@ export const login2 = async (req: Request, res: Response) => {
await checkUserDevice({
user,
ip: req.realIP,
userAgent: req.headers['user-agent'] ?? ''
userAgent: req.headers["user-agent"] ?? "",
});
const tokens = await issueAuthTokens({
userId: user._id,
ip: req.realIP,
userAgent: req.headers['user-agent'] ?? ''
userAgent: req.headers["user-agent"] ?? "",
});
// store (refresh) token in httpOnly cookie
res.cookie('jid', tokens.refreshToken, {
res.cookie("jid", tokens.refreshToken, {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: await getHttpsEnabled()
path: "/",
sameSite: "strict",
secure: await getHttpsEnabled(),
});
const loginAction = await EELogService.createAction({
name: ACTION_LOGIN,
userId: user._id
userId: user._id,
});
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers['user-agent']),
ipAddress: req.realIP
channel: getChannelFromUserAgent(req.headers["user-agent"]),
ipAddress: req.realIP,
});
// return (access) token in response
@ -152,12 +152,12 @@ export const login2 = async (req: Request, res: Response) => {
publicKey: user.publicKey,
encryptedPrivateKey: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag
tag: user.tag,
});
}
return res.status(400).send({
message: 'Failed to authenticate. Try again?'
message: "Failed to authenticate. Try again?",
});
}
);
@ -175,51 +175,51 @@ export const logout = async (req: Request, res: Response) => {
}
// clear httpOnly cookie
res.cookie('jid', '', {
res.cookie("jid", "", {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: (await getHttpsEnabled()) as boolean
path: "/",
sameSite: "strict",
secure: (await getHttpsEnabled()) as boolean,
});
const logoutAction = await EELogService.createAction({
name: ACTION_LOGOUT,
userId: req.user._id
userId: req.user._id,
});
logoutAction && await EELogService.createLog({
userId: req.user._id,
actions: [logoutAction],
channel: getChannelFromUserAgent(req.headers['user-agent']),
ipAddress: req.realIP
channel: getChannelFromUserAgent(req.headers["user-agent"]),
ipAddress: req.realIP,
});
return res.status(200).send({
message: 'Successfully logged out.'
message: "Successfully logged out.",
});
};
export const getCommonPasswords = async (req: Request, res: Response) => {
const commonPasswords = fs.readFileSync(
path.resolve(__dirname, '../../data/' + 'common_passwords.txt'),
'utf8'
).split('\n');
path.resolve(__dirname, "../../data/" + "common_passwords.txt"),
"utf8"
).split("\n");
return res.status(200).send(commonPasswords);
}
export const revokeAllSessions = async (req: Request, res: Response) => {
await TokenVersion.updateMany({
user: req.user._id
user: req.user._id,
}, {
$inc: {
refreshVersion: 1,
accessVersion: 1
}
accessVersion: 1,
},
});
return res.status(200).send({
message: 'Successfully revoked all sessions.'
message: "Successfully revoked all sessions.",
});
}
@ -231,7 +231,7 @@ export const revokeAllSessions = async (req: Request, res: Response) => {
*/
export const checkAuth = async (req: Request, res: Response) => {
return res.status(200).send({
message: 'Authenticated'
message: "Authenticated",
});
}
@ -245,7 +245,7 @@ export const getNewToken = async (req: Request, res: Response) => {
const refreshToken = req.cookies.jid;
if (!refreshToken) {
throw new Error('Failed to find refresh token in request cookies');
throw new Error("Failed to find refresh token in request cookies");
}
const decodedToken = <jwt.UserIDJwtPayload>(
@ -253,35 +253,35 @@ export const getNewToken = async (req: Request, res: Response) => {
);
const user = await User.findOne({
_id: decodedToken.userId
}).select('+publicKey +refreshVersion +accessVersion');
_id: decodedToken.userId,
}).select("+publicKey +refreshVersion +accessVersion");
if (!user) throw new Error('Failed to authenticate unfound user');
if (!user) throw new Error("Failed to authenticate unfound user");
if (!user?.publicKey)
throw new Error('Failed to authenticate not fully set up account');
throw new Error("Failed to authenticate not fully set up account");
const tokenVersion = await TokenVersion.findById(decodedToken.tokenVersionId);
if (!tokenVersion) throw UnauthorizedRequestError({
message: 'Failed to validate refresh token'
message: "Failed to validate refresh token",
});
if (decodedToken.refreshVersion !== tokenVersion.refreshVersion) throw BadRequestError({
message: 'Failed to validate refresh token'
message: "Failed to validate refresh token",
});
const token = createToken({
payload: {
userId: decodedToken.userId,
tokenVersionId: tokenVersion._id.toString(),
accessVersion: tokenVersion.refreshVersion
accessVersion: tokenVersion.refreshVersion,
},
expiresIn: await getJwtAuthLifetime(),
secret: await getJwtAuthSecret()
secret: await getJwtAuthSecret(),
});
return res.status(200).send({
token
token,
});
};

@ -1,7 +1,7 @@
import { Request, Response } from 'express';
import { Types } from 'mongoose';
import { Bot, BotKey } from '../../models';
import { createBot } from '../../helpers/bot';
import { Request, Response } from "express";
import { Types } from "mongoose";
import { Bot, BotKey } from "../../models";
import { createBot } from "../../helpers/bot";
interface BotKey {
encryptedKey: string;
@ -19,20 +19,20 @@ export const getBotByWorkspaceId = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
let bot = await Bot.findOne({
workspace: workspaceId
workspace: workspaceId,
});
if (!bot) {
// case: bot doesn't exist for workspace with id [workspaceId]
// -> create a new bot and return it
bot = await createBot({
name: 'Infisical Bot',
workspaceId: new Types.ObjectId(workspaceId)
name: "Infisical Bot",
workspaceId: new Types.ObjectId(workspaceId),
});
}
return res.status(200).send({
bot
bot,
});
};
@ -49,40 +49,40 @@ export const setBotActiveState = async (req: Request, res: Response) => {
// bot state set to active -> share workspace key with bot
if (!botKey?.encryptedKey || !botKey?.nonce) {
return res.status(400).send({
message: 'Failed to set bot state to active - missing bot key'
message: "Failed to set bot state to active - missing bot key",
});
}
await BotKey.findOneAndUpdate({
workspace: req.bot.workspace
workspace: req.bot.workspace,
}, {
encryptedKey: botKey.encryptedKey,
nonce: botKey.nonce,
sender: req.user._id,
bot: req.bot._id,
workspace: req.bot.workspace
workspace: req.bot.workspace,
}, {
upsert: true,
new: true
new: true,
});
} else {
// case: bot state set to inactive -> delete bot's workspace key
await BotKey.deleteOne({
bot: req.bot._id
bot: req.bot._id,
});
}
let bot = await Bot.findOneAndUpdate({
_id: req.bot._id
const bot = await Bot.findOneAndUpdate({
_id: req.bot._id,
}, {
isActive
isActive,
}, {
new: true
new: true,
});
if (!bot) throw new Error('Failed to update bot active state');
if (!bot) throw new Error("Failed to update bot active state");
return res.status(200).send({
bot
bot,
});
};

@ -1,19 +1,19 @@
import * as authController from './authController';
import * as botController from './botController';
import * as integrationAuthController from './integrationAuthController';
import * as integrationController from './integrationController';
import * as keyController from './keyController';
import * as membershipController from './membershipController';
import * as membershipOrgController from './membershipOrgController';
import * as organizationController from './organizationController';
import * as passwordController from './passwordController';
import * as secretController from './secretController';
import * as serviceTokenController from './serviceTokenController';
import * as signupController from './signupController';
import * as stripeController from './stripeController';
import * as userActionController from './userActionController';
import * as userController from './userController';
import * as workspaceController from './workspaceController';
import * as authController from "./authController";
import * as botController from "./botController";
import * as integrationAuthController from "./integrationAuthController";
import * as integrationController from "./integrationController";
import * as keyController from "./keyController";
import * as membershipController from "./membershipController";
import * as membershipOrgController from "./membershipOrgController";
import * as organizationController from "./organizationController";
import * as passwordController from "./passwordController";
import * as secretController from "./secretController";
import * as serviceTokenController from "./serviceTokenController";
import * as signupController from "./signupController";
import * as stripeController from "./stripeController";
import * as userActionController from "./userActionController";
import * as userController from "./userController";
import * as workspaceController from "./workspaceController";
export {
authController,
@ -31,5 +31,5 @@ export {
stripeController,
userActionController,
userController,
workspaceController
workspaceController,
};

@ -1,21 +1,17 @@
import { Request, Response } from 'express';
import { Types } from 'mongoose';
import { Request, Response } from "express";
import { Types } from "mongoose";
import { standardRequest } from "../../config/request";
import { getApps, getTeams, revokeAccess } from "../../integrations";
import { Bot, IntegrationAuth } from "../../models";
import { IntegrationService } from "../../services";
import {
IntegrationAuth,
Bot
} from '../../models';
import { ALGORITHM_AES_256_GCM, ENCODING_SCHEME_UTF8, INTEGRATION_SET, getIntegrationOptions as getIntegrationOptionsFunc } from '../../variables';
import { IntegrationService } from '../../services';
import {
getApps,
getTeams,
revokeAccess
} from '../../integrations';
import {
INTEGRATION_VERCEL_API_URL,
INTEGRATION_RAILWAY_API_URL
} from '../../variables';
import { standardRequest } from '../../config/request';
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
INTEGRATION_RAILWAY_API_URL,
INTEGRATION_SET,
INTEGRATION_VERCEL_API_URL,
getIntegrationOptions as getIntegrationOptionsFunc
} from "../../variables";
/***
* Return integration authorization with id [integrationAuthId]
@ -23,22 +19,23 @@ import { standardRequest } from '../../config/request';
export const getIntegrationAuth = async (req: Request, res: Response) => {
const { integrationAuthId } = req.params;
const integrationAuth = await IntegrationAuth.findById(integrationAuthId);
if (!integrationAuth) return res.status(400).send({
message: 'Failed to find integration authorization'
});
return res.status(200).send({
integrationAuth
});
}
if (!integrationAuth)
return res.status(400).send({
message: "Failed to find integration authorization"
});
return res.status(200).send({
integrationAuth
});
};
export const getIntegrationOptions = async (req: Request, res: Response) => {
const INTEGRATION_OPTIONS = await getIntegrationOptionsFunc();
const INTEGRATION_OPTIONS = await getIntegrationOptionsFunc();
return res.status(200).send({
integrationOptions: INTEGRATION_OPTIONS,
});
return res.status(200).send({
integrationOptions: INTEGRATION_OPTIONS
});
};
/**
@ -47,26 +44,22 @@ export const getIntegrationOptions = async (req: Request, res: Response) => {
* @param res
* @returns
*/
export const oAuthExchange = async (
req: Request,
res: Response
) => {
export const oAuthExchange = async (req: Request, res: Response) => {
const { workspaceId, code, integration } = req.body;
if (!INTEGRATION_SET.has(integration))
throw new Error('Failed to validate integration');
if (!INTEGRATION_SET.has(integration)) throw new Error("Failed to validate integration");
const environments = req.membership.workspace?.environments || [];
if(environments.length === 0){
throw new Error("Failed to get environments")
if (environments.length === 0) {
throw new Error("Failed to get environments");
}
const integrationAuth = await IntegrationService.handleOAuthExchange({
workspaceId,
integration,
code,
environment: environments[0].slug,
environment: environments[0].slug
});
return res.status(200).send({
integrationAuth
});
@ -75,69 +68,70 @@ export const oAuthExchange = async (
/**
* Save integration access token and (optionally) access id as part of integration
* [integration] for workspace with id [workspaceId]
* @param req
* @param res
* @param req
* @param res
*/
export const saveIntegrationAccessToken = async (
req: Request,
res: Response
) => {
// TODO: refactor
// TODO: check if access token is valid for each integration
export const saveIntegrationAccessToken = async (req: Request, res: Response) => {
// TODO: refactor
// TODO: check if access token is valid for each integration
let integrationAuth;
const {
workspaceId,
accessId,
accessToken,
url,
namespace,
integration
}: {
workspaceId: string;
accessId: string | null;
accessToken: string;
url: string;
namespace: string;
integration: string;
} = req.body;
let integrationAuth;
const {
workspaceId,
accessId,
accessToken,
url,
namespace,
integration
}: {
workspaceId: string;
accessId: string | null;
accessToken: string;
url: string;
namespace: string;
integration: string;
} = req.body;
const bot = await Bot.findOne({
workspace: new Types.ObjectId(workspaceId),
isActive: true
});
if (!bot) throw new Error('Bot must be enabled to save integration access token');
const bot = await Bot.findOne({
workspace: new Types.ObjectId(workspaceId),
isActive: true
});
integrationAuth = await IntegrationAuth.findOneAndUpdate({
workspace: new Types.ObjectId(workspaceId),
integration
}, {
workspace: new Types.ObjectId(workspaceId),
integration,
url,
namespace,
algorithm: ALGORITHM_AES_256_GCM,
keyEncoding: ENCODING_SCHEME_UTF8
}, {
new: true,
upsert: true
});
// encrypt and save integration access details
integrationAuth = await IntegrationService.setIntegrationAuthAccess({
integrationAuthId: integrationAuth._id.toString(),
accessId,
accessToken,
accessExpiresAt: undefined
});
if (!integrationAuth) throw new Error('Failed to save integration access token');
return res.status(200).send({
integrationAuth
});
}
if (!bot) throw new Error("Bot must be enabled to save integration access token");
integrationAuth = await IntegrationAuth.findOneAndUpdate(
{
workspace: new Types.ObjectId(workspaceId),
integration
},
{
workspace: new Types.ObjectId(workspaceId),
integration,
url,
namespace,
algorithm: ALGORITHM_AES_256_GCM,
keyEncoding: ENCODING_SCHEME_UTF8
},
{
new: true,
upsert: true
}
);
// encrypt and save integration access details
integrationAuth = await IntegrationService.setIntegrationAuthAccess({
integrationAuthId: integrationAuth._id.toString(),
accessId,
accessToken,
accessExpiresAt: undefined
});
if (!integrationAuth) throw new Error("Failed to save integration access token");
return res.status(200).send({
integrationAuth
});
};
/**
* Return list of applications allowed for integration with integration authorization id [integrationAuthId]
@ -147,108 +141,108 @@ export const saveIntegrationAccessToken = async (
*/
export const getIntegrationAuthApps = async (req: Request, res: Response) => {
const teamId = req.query.teamId as string;
const apps = await getApps({
integrationAuth: req.integrationAuth,
accessToken: req.accessToken,
accessId: req.accessId,
...teamId && { teamId }
accessId: req.accessId,
...(teamId && { teamId })
});
return res.status(200).send({
apps
});
return res.status(200).send({
apps
});
};
/**
* Return list of teams allowed for integration with integration authorization id [integrationAuthId]
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const getIntegrationAuthTeams = async (req: Request, res: Response) => {
const teams = await getTeams({
integrationAuth: req.integrationAuth,
accessToken: req.accessToken
});
return res.status(200).send({
teams
});
}
const teams = await getTeams({
integrationAuth: req.integrationAuth,
accessToken: req.accessToken
});
return res.status(200).send({
teams
});
};
/**
* Return list of available Vercel (preview) branches for Vercel project with
* id [appId]
* @param req
* @param res
* @param req
* @param res
*/
export const getIntegrationAuthVercelBranches = async (req: Request, res: Response) => {
const { integrationAuthId } = req.params;
const appId = req.query.appId as string;
interface VercelBranch {
ref: string;
lastCommit: string;
isProtected: boolean;
}
const appId = req.query.appId as string;
const params = new URLSearchParams({
projectId: appId,
...(req.integrationAuth.teamId ? {
teamId: req.integrationAuth.teamId
} : {})
});
interface VercelBranch {
ref: string;
lastCommit: string;
isProtected: boolean;
}
let branches: string[] = [];
if (appId && appId !== '') {
const { data }: { data: VercelBranch[] } = await standardRequest.get(
`${INTEGRATION_VERCEL_API_URL}/v1/integrations/git-branches`,
{
params,
headers: {
Authorization: `Bearer ${req.accessToken}`,
'Accept-Encoding': 'application/json'
}
}
);
branches = data.map((b) => b.ref);
}
const params = new URLSearchParams({
projectId: appId,
...(req.integrationAuth.teamId
? {
teamId: req.integrationAuth.teamId
}
: {})
});
return res.status(200).send({
branches
});
}
let branches: string[] = [];
if (appId && appId !== "") {
const { data }: { data: VercelBranch[] } = await standardRequest.get(
`${INTEGRATION_VERCEL_API_URL}/v1/integrations/git-branches`,
{
params,
headers: {
Authorization: `Bearer ${req.accessToken}`,
"Accept-Encoding": "application/json"
}
}
);
branches = data.map((b) => b.ref);
}
return res.status(200).send({
branches
});
};
/**
* Return list of Railway environments for Railway project with
* id [appId]
* @param req
* @param res
* @param req
* @param res
*/
export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: Response) => {
const { integrationAuthId } = req.params;
const appId = req.query.appId as string;
interface RailwayEnvironment {
node: {
id: string;
name: string;
isEphemeral: boolean;
}
}
interface Environment {
environmentId: string;
name: string;
}
let environments: Environment[] = [];
const appId = req.query.appId as string;
if (appId && appId !== '') {
const query = `
interface RailwayEnvironment {
node: {
id: string;
name: string;
isEphemeral: boolean;
};
}
interface Environment {
environmentId: string;
name: string;
}
let environments: Environment[] = [];
if (appId && appId !== "") {
const query = `
query GetEnvironments($projectId: String!, $after: String, $before: String, $first: Int, $isEphemeral: Boolean, $last: Int) {
environments(projectId: $projectId, after: $after, before: $before, first: $first, isEphemeral: $isEphemeral, last: $last) {
edges {
@ -261,59 +255,68 @@ export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: R
}
}
`;
const variables = {
projectId: appId
}
const { data: { data: { environments: { edges } } } } = await standardRequest.post(INTEGRATION_RAILWAY_API_URL, {
query,
variables,
}, {
headers: {
'Authorization': `Bearer ${req.accessToken}`,
'Content-Type': 'application/json',
},
});
environments = edges.map((e: RailwayEnvironment) => {
return ({
name: e.node.name,
environmentId: e.node.id
});
});
}
return res.status(200).send({
environments
});
}
const variables = {
projectId: appId
};
const {
data: {
data: {
environments: { edges }
}
}
} = await standardRequest.post(
INTEGRATION_RAILWAY_API_URL,
{
query,
variables
},
{
headers: {
Authorization: `Bearer ${req.accessToken}`,
"Content-Type": "application/json"
}
}
);
environments = edges.map((e: RailwayEnvironment) => {
return {
name: e.node.name,
environmentId: e.node.id
};
});
}
return res.status(200).send({
environments
});
};
/**
* Return list of Railway services for Railway project with id
* [appId]
* @param req
* @param res
* @param req
* @param res
*/
export const getIntegrationAuthRailwayServices = async (req: Request, res: Response) => {
const { integrationAuthId } = req.params;
const appId = req.query.appId as string;
interface RailwayService {
node: {
id: string;
name: string;
}
}
interface Service {
name: string;
serviceId: string;
}
let services: Service[] = [];
const query = `
const appId = req.query.appId as string;
interface RailwayService {
node: {
id: string;
name: string;
};
}
interface Service {
name: string;
serviceId: string;
}
let services: Service[] = [];
const query = `
query project($id: String!) {
project(id: $id) {
createdAt
@ -341,31 +344,43 @@ export const getIntegrationAuthRailwayServices = async (req: Request, res: Respo
}
`;
if (appId && appId !== '') {
const variables = {
id: appId
}
const { data: { data: { project: { services: { edges } } } } } = await standardRequest.post(INTEGRATION_RAILWAY_API_URL, {
query,
variables
}, {
headers: {
'Authorization': `Bearer ${req.accessToken}`,
'Content-Type': 'application/json',
},
});
services = edges.map((e: RailwayService) => ({
name: e.node.name,
serviceId: e.node.id
}));
}
return res.status(200).send({
services
});
}
if (appId && appId !== "") {
const variables = {
id: appId
};
const {
data: {
data: {
project: {
services: { edges }
}
}
}
} = await standardRequest.post(
INTEGRATION_RAILWAY_API_URL,
{
query,
variables
},
{
headers: {
Authorization: `Bearer ${req.accessToken}`,
"Content-Type": "application/json"
}
}
);
services = edges.map((e: RailwayService) => ({
name: e.node.name,
serviceId: e.node.id
}));
}
return res.status(200).send({
services
});
};
/**
* Delete integration authorization with id [integrationAuthId]
@ -376,10 +391,10 @@ export const getIntegrationAuthRailwayServices = async (req: Request, res: Respo
export const deleteIntegrationAuth = async (req: Request, res: Response) => {
const integrationAuth = await revokeAccess({
integrationAuth: req.integrationAuth,
accessToken: req.accessToken,
accessToken: req.accessToken
});
return res.status(200).send({
integrationAuth,
integrationAuth
});
};

@ -1,6 +1,6 @@
import { Request, Response } from 'express';
import { Key } from '../../models';
import { findMembership } from '../../helpers/membership';
import { Request, Response } from "express";
import { Key } from "../../models";
import { findMembership } from "../../helpers/membership";
/**
* Add (encrypted) copy of workspace key for workspace with id [workspaceId] for user with
@ -16,11 +16,11 @@ export const uploadKey = async (req: Request, res: Response) => {
// validate membership of receiver
const receiverMembership = await findMembership({
user: key.userId,
workspace: workspaceId
workspace: workspaceId,
});
if (!receiverMembership) {
throw new Error('Failed receiver membership validation for workspace');
throw new Error("Failed receiver membership validation for workspace");
}
await new Key({
@ -28,11 +28,11 @@ export const uploadKey = async (req: Request, res: Response) => {
nonce: key.nonce,
sender: req.user._id,
receiver: key.userId,
workspace: workspaceId
workspace: workspaceId,
}).save();
return res.status(200).send({
message: 'Successfully uploaded key to workspace'
message: "Successfully uploaded key to workspace",
});
};
@ -48,16 +48,16 @@ export const getLatestKey = async (req: Request, res: Response) => {
// get latest key
const latestKey = await Key.find({
workspace: workspaceId,
receiver: req.user._id
receiver: req.user._id,
})
.sort({ createdAt: -1 })
.limit(1)
.populate('sender', '+publicKey');
.populate("sender", "+publicKey");
const resObj: any = {};
if (latestKey.length > 0) {
resObj['latestKey'] = latestKey[0];
resObj["latestKey"] = latestKey[0];
}
return res.status(200).send(resObj);

@ -1,12 +1,9 @@
import { Request, Response } from 'express';
import { Membership, MembershipOrg, User, Key } from '../../models';
import {
findMembership,
deleteMembership as deleteMember
} from '../../helpers/membership';
import { sendMail } from '../../helpers/nodemailer';
import { ADMIN, MEMBER, ACCEPTED } from '../../variables';
import { getSiteURL } from '../../config';
import { Request, Response } from "express";
import { Key, Membership, MembershipOrg, User } from "../../models";
import { deleteMembership as deleteMember, findMembership } from "../../helpers/membership";
import { sendMail } from "../../helpers/nodemailer";
import { ACCEPTED, ADMIN, MEMBER } from "../../variables";
import { getSiteURL } from "../../config";
/**
* Check that user is a member of workspace with id [workspaceId]
@ -23,12 +20,12 @@ export const validateMembership = async (req: Request, res: Response) => {
});
if (!membership) {
throw new Error('Failed to validate membership');
throw new Error("Failed to validate membership");
}
return res.status(200).send({
message: 'Workspace membership confirmed'
});
return res.status(200).send({
message: "Workspace membership confirmed"
});
};
/**
@ -43,12 +40,10 @@ export const deleteMembership = async (req: Request, res: Response) => {
// check if membership to delete exists
const membershipToDelete = await Membership.findOne({
_id: membershipId
}).populate('user');
}).populate("user");
if (!membershipToDelete) {
throw new Error(
"Failed to delete workspace membership that doesn't exist"
);
throw new Error("Failed to delete workspace membership that doesn't exist");
}
// check if user is a member and admin of the workspace
@ -59,12 +54,12 @@ export const deleteMembership = async (req: Request, res: Response) => {
});
if (!membership) {
throw new Error('Failed to validate workspace membership');
throw new Error("Failed to validate workspace membership");
}
if (membership.role !== ADMIN) {
// user is not an admin member of the workspace
throw new Error('Insufficient role for deleting workspace membership');
throw new Error("Insufficient role for deleting workspace membership");
}
// delete workspace membership
@ -72,9 +67,9 @@ export const deleteMembership = async (req: Request, res: Response) => {
membershipId: membershipToDelete._id.toString()
});
return res.status(200).send({
deletedMembership
});
return res.status(200).send({
deletedMembership
});
};
/**
@ -88,7 +83,7 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
const { role } = req.body;
if (![ADMIN, MEMBER].includes(role)) {
throw new Error('Failed to validate role');
throw new Error("Failed to validate role");
}
// validate target membership
@ -97,7 +92,7 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
});
if (!membershipToChangeRole) {
throw new Error('Failed to find membership to change role');
throw new Error("Failed to find membership to change role");
}
// check if user is a member and admin of target membership's
@ -108,20 +103,20 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
});
if (!membership) {
throw new Error('Failed to validate membership');
throw new Error("Failed to validate membership");
}
if (membership.role !== ADMIN) {
// user is not an admin member of the workspace
throw new Error('Insufficient role for changing member roles');
throw new Error("Insufficient role for changing member roles");
}
membershipToChangeRole.role = role;
await membershipToChangeRole.save();
return res.status(200).send({
membership: membershipToChangeRole
});
return res.status(200).send({
membership: membershipToChangeRole
});
};
/**
@ -136,10 +131,9 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
const invitee = await User.findOne({
email
}).select('+publicKey');
}).select("+publicKey");
if (!invitee || !invitee?.publicKey)
throw new Error('Failed to validate invitee');
if (!invitee || !invitee?.publicKey) throw new Error("Failed to validate invitee");
// validate invitee's workspace membership - ensure member isn't
// already a member of the workspace
@ -148,8 +142,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
workspace: workspaceId
});
if (inviteeMembership)
throw new Error('Failed to add existing member of workspace');
if (inviteeMembership) throw new Error("Failed to add existing member of workspace");
// validate invitee's organization membership - ensure that only
// (accepted) organization members can be added to the workspace
@ -159,8 +152,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
status: ACCEPTED
});
if (!membershipOrg)
throw new Error("Failed to validate invitee's organization membership");
if (!membershipOrg) throw new Error("Failed to validate invitee's organization membership");
// get latest key
const latestKey = await Key.findOne({
@ -168,29 +160,29 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
receiver: req.user._id
})
.sort({ createdAt: -1 })
.populate('sender', '+publicKey');
.populate("sender", "+publicKey");
// create new workspace membership
const m = await new Membership({
await new Membership({
user: invitee._id,
workspace: workspaceId,
role: MEMBER
}).save();
await sendMail({
template: 'workspaceInvitation.handlebars',
subjectLine: 'Infisical workspace invitation',
template: "workspaceInvitation.handlebars",
subjectLine: "Infisical workspace invitation",
recipients: [invitee.email],
substitutions: {
inviterFirstName: req.user.firstName,
inviterEmail: req.user.email,
workspaceName: req.membership.workspace.name,
callback_url: (await getSiteURL()) + '/login'
callback_url: (await getSiteURL()) + "/login"
}
});
return res.status(200).send({
invitee,
latestKey
});
return res.status(200).send({
invitee,
latestKey
});
};

@ -1,15 +1,27 @@
import { Types } from 'mongoose';
import { Request, Response } from 'express';
import { MembershipOrg, Organization, User } from '../../models';
import { deleteMembershipOrg as deleteMemberFromOrg } from '../../helpers/membershipOrg';
import { createToken } from '../../helpers/auth';
import { updateSubscriptionOrgQuantity } from '../../helpers/organization';
import { sendMail } from '../../helpers/nodemailer';
import { TokenService } from '../../services';
import { EELicenseService } from '../../ee/services';
import { OWNER, ADMIN, MEMBER, ACCEPTED, INVITED, TOKEN_EMAIL_ORG_INVITATION } from '../../variables';
import { getSiteURL, getJwtSignupLifetime, getJwtSignupSecret, getSmtpConfigured } from '../../config';
import { validateUserEmail } from '../../validation';
import { Types } from "mongoose";
import { Request, Response } from "express";
import { MembershipOrg, Organization, User } from "../../models";
import { deleteMembershipOrg as deleteMemberFromOrg } from "../../helpers/membershipOrg";
import { createToken } from "../../helpers/auth";
import { updateSubscriptionOrgQuantity } from "../../helpers/organization";
import { sendMail } from "../../helpers/nodemailer";
import { TokenService } from "../../services";
import { EELicenseService } from "../../ee/services";
import {
ACCEPTED,
ADMIN,
INVITED,
MEMBER,
OWNER,
TOKEN_EMAIL_ORG_INVITATION
} from "../../variables";
import {
getJwtSignupLifetime,
getJwtSignupSecret,
getSiteURL,
getSmtpConfigured
} from "../../config";
import { validateUserEmail } from "../../validation";
/**
* Delete organization membership with id [membershipOrgId] from organization
@ -17,18 +29,16 @@ import { validateUserEmail } from '../../validation';
* @param res
* @returns
*/
export const deleteMembershipOrg = async (req: Request, res: Response) => {
export const deleteMembershipOrg = async (req: Request, _res: Response) => {
const { membershipOrgId } = req.params;
// check if organization membership to delete exists
const membershipOrgToDelete = await MembershipOrg.findOne({
_id: membershipOrgId
}).populate('user');
}).populate("user");
if (!membershipOrgToDelete) {
throw new Error(
"Failed to delete organization membership that doesn't exist"
);
throw new Error("Failed to delete organization membership that doesn't exist");
}
// check if user is a member and admin of the organization
@ -39,16 +49,16 @@ export const deleteMembershipOrg = async (req: Request, res: Response) => {
});
if (!membershipOrg) {
throw new Error('Failed to validate organization membership');
throw new Error("Failed to validate organization membership");
}
if (membershipOrg.role !== OWNER && membershipOrg.role !== ADMIN) {
// user is not an admin member of the organization
throw new Error('Insufficient role for deleting organization membership');
throw new Error("Insufficient role for deleting organization membership");
}
// delete organization membership
const deletedMembershipOrg = await deleteMemberFromOrg({
await deleteMemberFromOrg({
membershipOrgId: membershipOrgToDelete._id.toString()
});
@ -56,7 +66,7 @@ export const deleteMembershipOrg = async (req: Request, res: Response) => {
organizationId: membershipOrg.organization.toString()
});
return membershipOrgToDelete;
return membershipOrgToDelete;
};
/**
@ -66,14 +76,14 @@ export const deleteMembershipOrg = async (req: Request, res: Response) => {
* @returns
*/
export const changeMembershipOrgRole = async (req: Request, res: Response) => {
// change role for (target) organization membership with id
// [membershipOrgId]
// change role for (target) organization membership with id
// [membershipOrgId]
let membershipToChangeRole;
let membershipToChangeRole;
return res.status(200).send({
membershipOrg: membershipToChangeRole
});
return res.status(200).send({
membershipOrg: membershipToChangeRole
});
};
/**
@ -84,7 +94,7 @@ export const changeMembershipOrgRole = async (req: Request, res: Response) => {
* @returns
*/
export const inviteUserToOrganization = async (req: Request, res: Response) => {
let invitee, inviteeMembershipOrg, completeInviteLink;
let inviteeMembershipOrg, completeInviteLink;
const { organizationId, inviteeEmail } = req.body;
const host = req.headers.host;
const siteUrl = `${req.protocol}://${host}`;
@ -96,25 +106,26 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
});
if (!membershipOrg) {
throw new Error('Failed to validate organization membership');
throw new Error("Failed to validate organization membership");
}
const plan = await EELicenseService.getPlan(organizationId);
if (plan.memberLimit !== null) {
// case: limit imposed on number of members allowed
if (plan.membersUsed >= plan.memberLimit) {
// case: number of members used exceeds the number of members allowed
return res.status(400).send({
message: 'Failed to invite member due to member limit reached. Upgrade plan to invite more members.'
message:
"Failed to invite member due to member limit reached. Upgrade plan to invite more members."
});
}
}
invitee = await User.findOne({
const invitee = await User.findOne({
email: inviteeEmail
}).select('+publicKey');
}).select("+publicKey");
if (invitee) {
// case: invitee is an existing user
@ -125,13 +136,10 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
});
if (inviteeMembershipOrg && inviteeMembershipOrg.status === ACCEPTED) {
throw new Error(
'Failed to invite an existing member of the organization'
);
throw new Error("Failed to invite an existing member of the organization");
}
if (!inviteeMembershipOrg) {
await new MembershipOrg({
user: invitee,
inviteEmail: inviteeEmail,
@ -149,7 +157,7 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
if (!inviteeMembershipOrg) {
// case: invitee has never been invited before
// validate that email is not disposable
validateUserEmail(inviteeEmail);
@ -165,7 +173,6 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
const organization = await Organization.findOne({ _id: organizationId });
if (organization) {
const token = await TokenService.createToken({
type: TOKEN_EMAIL_ORG_INVITATION,
email: inviteeEmail,
@ -173,8 +180,8 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
});
await sendMail({
template: 'organizationInvitation.handlebars',
subjectLine: 'Infisical organization invitation',
template: "organizationInvitation.handlebars",
subjectLine: "Infisical organization invitation",
recipients: [inviteeEmail],
substitutions: {
inviterFirstName: req.user.firstName,
@ -183,21 +190,23 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
email: inviteeEmail,
organizationId: organization._id.toString(),
token,
callback_url: (await getSiteURL()) + '/signupinvite'
callback_url: (await getSiteURL()) + "/signupinvite"
}
});
if (!(await getSmtpConfigured())) {
completeInviteLink = `${siteUrl + '/signupinvite'}?token=${token}&to=${inviteeEmail}&organization_id=${organization._id}`
completeInviteLink = `${
siteUrl + "/signupinvite"
}?token=${token}&to=${inviteeEmail}&organization_id=${organization._id}`;
}
}
await updateSubscriptionOrgQuantity({ organizationId });
return res.status(200).send({
message: `Sent an invite link to ${req.body.inviteeEmail}`,
completeInviteLink
});
return res.status(200).send({
message: `Sent an invite link to ${req.body.inviteeEmail}`,
completeInviteLink
});
};
/**
@ -208,14 +217,10 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
* @returns
*/
export const verifyUserToOrganization = async (req: Request, res: Response) => {
let user;
const {
email,
organizationId,
code
} = req.body;
let user;
const { email, organizationId, code } = req.body;
user = await User.findOne({ email }).select('+publicKey');
user = await User.findOne({ email }).select("+publicKey");
const membershipOrg = await MembershipOrg.findOne({
inviteEmail: email,
@ -223,8 +228,7 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
organization: new Types.ObjectId(organizationId)
});
if (!membershipOrg)
throw new Error('Failed to find any invitations for email');
if (!membershipOrg) throw new Error("Failed to find any invitations for email");
await TokenService.validateToken({
type: TOKEN_EMAIL_ORG_INVITATION,
@ -238,14 +242,14 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
// membership can be approved and redirected to login/dashboard
membershipOrg.status = ACCEPTED;
await membershipOrg.save();
await updateSubscriptionOrgQuantity({
organizationId
});
return res.status(200).send({
message: 'Successfully verified email',
user,
message: "Successfully verified email",
user
});
}
@ -265,9 +269,9 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
secret: await getJwtSignupSecret()
});
return res.status(200).send({
message: 'Successfully verified email',
user,
token
});
return res.status(200).send({
message: "Successfully verified email",
user,
token
});
};

@ -1,28 +1,27 @@
import { Request, Response } from 'express';
import Stripe from 'stripe';
import { Request, Response } from "express";
import Stripe from "stripe";
import {
IncidentContactOrg,
Membership,
MembershipOrg,
Organization,
Workspace,
IncidentContactOrg
} from '../../models';
import { createOrganization as create } from '../../helpers/organization';
import { addMembershipsOrg } from '../../helpers/membershipOrg';
import { OWNER, ACCEPTED } from '../../variables';
import _ from 'lodash';
import { getStripeSecretKey, getSiteURL } from '../../config';
} from "../../models";
import { createOrganization as create } from "../../helpers/organization";
import { addMembershipsOrg } from "../../helpers/membershipOrg";
import { ACCEPTED, OWNER } from "../../variables";
import { getSiteURL, getStripeSecretKey } from "../../config";
export const getOrganizations = async (req: Request, res: Response) => {
const organizations = (
await MembershipOrg.find({
user: req.user._id,
status: ACCEPTED
}).populate('organization')
status: ACCEPTED,
}).populate("organization")
).map((m) => m.organization);
return res.status(200).send({
organizations
organizations,
});
};
@ -37,24 +36,24 @@ export const createOrganization = async (req: Request, res: Response) => {
const { organizationName } = req.body;
if (organizationName.length < 1) {
throw new Error('Organization names must be at least 1-character long');
throw new Error("Organization names must be at least 1-character long");
}
// create organization and add user as member
const organization = await create({
email: req.user.email,
name: organizationName
name: organizationName,
});
await addMembershipsOrg({
userIds: [req.user._id.toString()],
organizationId: organization._id.toString(),
roles: [OWNER],
statuses: [ACCEPTED]
statuses: [ACCEPTED],
});
return res.status(200).send({
organization
organization,
});
};
@ -67,7 +66,7 @@ export const createOrganization = async (req: Request, res: Response) => {
export const getOrganization = async (req: Request, res: Response) => {
const organization = req.organization
return res.status(200).send({
organization
organization,
});
};
@ -81,11 +80,11 @@ export const getOrganizationMembers = async (req: Request, res: Response) => {
const { organizationId } = req.params;
const users = await MembershipOrg.find({
organization: organizationId
}).populate('user', '+publicKey');
organization: organizationId,
}).populate("user", "+publicKey");
return res.status(200).send({
users
users,
});
};
@ -105,23 +104,23 @@ export const getOrganizationWorkspaces = async (
(
await Workspace.find(
{
organization: organizationId
organization: organizationId,
},
'_id'
"_id"
)
).map((w) => w._id.toString())
);
const workspaces = (
await Membership.find({
user: req.user._id
}).populate('workspace')
user: req.user._id,
}).populate("workspace")
)
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
.map((m) => m.workspace);
return res.status(200).send({
workspaces
workspaces,
});
};
@ -137,19 +136,19 @@ export const changeOrganizationName = async (req: Request, res: Response) => {
const organization = await Organization.findOneAndUpdate(
{
_id: organizationId
_id: organizationId,
},
{
name
name,
},
{
new: true
new: true,
}
);
return res.status(200).send({
message: 'Successfully changed organization name',
organization
message: "Successfully changed organization name",
organization,
});
};
@ -166,11 +165,11 @@ export const getOrganizationIncidentContacts = async (
const { organizationId } = req.params;
const incidentContactsOrg = await IncidentContactOrg.find({
organization: organizationId
organization: organizationId,
});
return res.status(200).send({
incidentContactsOrg
incidentContactsOrg,
});
};
@ -194,7 +193,7 @@ export const addOrganizationIncidentContact = async (
);
return res.status(200).send({
incidentContactOrg
incidentContactOrg,
});
};
@ -213,12 +212,12 @@ export const deleteOrganizationIncidentContact = async (
const incidentContactOrg = await IncidentContactOrg.findOneAndDelete({
email,
organization: organizationId
organization: organizationId,
});
return res.status(200).send({
message: 'Successfully deleted organization incident contact',
incidentContactOrg
message: "Successfully deleted organization incident contact",
incidentContactOrg,
});
};
@ -235,28 +234,28 @@ export const createOrganizationPortalSession = async (
) => {
let session;
const stripe = new Stripe(await getStripeSecretKey(), {
apiVersion: '2022-08-01'
apiVersion: "2022-08-01",
});
// check if there is a payment method on file
const paymentMethods = await stripe.paymentMethods.list({
customer: req.organization.customerId,
type: 'card'
type: "card",
});
if (paymentMethods.data.length < 1) {
// case: no payment method on file
session = await stripe.checkout.sessions.create({
customer: req.organization.customerId,
mode: 'setup',
payment_method_types: ['card'],
success_url: (await getSiteURL()) + '/dashboard',
cancel_url: (await getSiteURL()) + '/dashboard'
mode: "setup",
payment_method_types: ["card"],
success_url: (await getSiteURL()) + "/dashboard",
cancel_url: (await getSiteURL()) + "/dashboard",
});
} else {
session = await stripe.billingPortal.sessions.create({
customer: req.organization.customerId,
return_url: (await getSiteURL()) + '/dashboard'
return_url: (await getSiteURL()) + "/dashboard",
});
}
@ -274,15 +273,15 @@ export const getOrganizationSubscriptions = async (
res: Response
) => {
const stripe = new Stripe(await getStripeSecretKey(), {
apiVersion: '2022-08-01'
apiVersion: "2022-08-01",
});
const subscriptions = await stripe.subscriptions.list({
customer: req.organization.customerId
customer: req.organization.customerId,
});
return res.status(200).send({
subscriptions
subscriptions,
});
};
@ -302,16 +301,16 @@ export const getOrganizationMembersAndTheirWorkspaces = async (
const workspacesSet = (
await Workspace.find(
{
organization: organizationId
organization: organizationId,
},
'_id'
"_id"
)
).map((w) => w._id.toString());
const memberships = (
await Membership.find({
workspace: { $in: workspacesSet }
}).populate('workspace')
workspace: { $in: workspacesSet },
}).populate("workspace")
);
const userToWorkspaceIds: any = {};

@ -1,85 +1,77 @@
import { Request, Response } from 'express';
import { Request, Response } from "express";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const jsrp = require('jsrp');
import * as bigintConversion from 'bigint-conversion';
import { User, BackupPrivateKey, LoginSRPDetail } from '../../models';
const jsrp = require("jsrp");
import * as bigintConversion from "bigint-conversion";
import { BackupPrivateKey, LoginSRPDetail, User } from "../../models";
import { clearTokens, createToken, sendMail } from "../../helpers";
import { TokenService } from "../../services";
import { AUTH_MODE_JWT, TOKEN_EMAIL_PASSWORD_RESET } from "../../variables";
import { BadRequestError } from "../../utils/errors";
import {
createToken,
sendMail,
clearTokens
} from '../../helpers';
import { TokenService } from '../../services';
import {
TOKEN_EMAIL_PASSWORD_RESET,
AUTH_MODE_JWT
} from '../../variables';
import { BadRequestError } from '../../utils/errors';
import {
getSiteURL,
getJwtSignupLifetime,
getJwtSignupSecret,
getHttpsEnabled
} from '../../config';
getHttpsEnabled,
getJwtSignupLifetime,
getJwtSignupSecret,
getSiteURL
} from "../../config";
/**
* Password reset step 1: Send email verification link to email [email]
* Password reset step 1: Send email verification link to email [email]
* for account recovery.
* @param req
* @param res
* @returns
*/
export const emailPasswordReset = async (req: Request, res: Response) => {
let email: string;
email = req.body.email;
const email: string = req.body.email;
const user = await User.findOne({ email }).select('+publicKey');
const user = await User.findOne({ email }).select("+publicKey");
if (!user || !user?.publicKey) {
// case: user has already completed account
return res.status(200).send({
message:"If an account exists with this email, a password reset link has been sent"
message: "If an account exists with this email, a password reset link has been sent"
});
}
const token = await TokenService.createToken({
type: TOKEN_EMAIL_PASSWORD_RESET,
email
});
await sendMail({
template: 'passwordReset.handlebars',
subjectLine: 'Infisical password reset',
template: "passwordReset.handlebars",
subjectLine: "Infisical password reset",
recipients: [email],
substitutions: {
email,
token,
callback_url: (await getSiteURL()) + '/password-reset'
callback_url: (await getSiteURL()) + "/password-reset"
}
});
return res.status(200).send({
message:"If an account exists with this email, a password reset link has been sent"
});
}
return res.status(200).send({
message: "If an account exists with this email, a password reset link has been sent"
});
};
/**
* Password reset step 2: Verify email verification link sent to email [email]
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const emailPasswordResetVerify = async (req: Request, res: Response) => {
const { email, code } = req.body;
const user = await User.findOne({ email }).select('+publicKey');
const user = await User.findOne({ email }).select("+publicKey");
if (!user || !user?.publicKey) {
// case: user doesn't exist with email [email] or
// case: user doesn't exist with email [email] or
// hasn't even completed their account
return res.status(403).send({
error: 'Failed email verification for password reset'
error: "Failed email verification for password reset"
});
}
await TokenService.validateToken({
type: TOKEN_EMAIL_PASSWORD_RESET,
email,
@ -95,12 +87,12 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => {
secret: await getJwtSignupSecret()
});
return res.status(200).send({
message: 'Successfully verified email',
user,
token
});
}
return res.status(200).send({
message: "Successfully verified email",
user,
token
});
};
/**
* Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol
@ -109,14 +101,14 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => {
* @returns
*/
export const srp1 = async (req: Request, res: Response) => {
// return salt, serverPublicKey as part of first step of SRP protocol
// return salt, serverPublicKey as part of first step of SRP protocol
const { clientPublicKey } = req.body;
const user = await User.findOne({
email: req.user.email
}).select('+salt +verifier');
}).select("+salt +verifier");
if (!user) throw new Error('Failed to find user');
if (!user) throw new Error("Failed to find user");
const server = new jsrp.server();
server.init(
@ -128,11 +120,15 @@ export const srp1 = async (req: Request, res: Response) => {
// generate server-side public key
const serverPublicKey = server.getPublicKey();
await LoginSRPDetail.findOneAndReplace({ email: req.user.email }, {
email: req.user.email,
clientPublicKey: clientPublicKey,
serverBInt: bigintConversion.bigintToBuf(server.bInt),
}, { upsert: true, returnNewDocument: false })
await LoginSRPDetail.findOneAndReplace(
{ email: req.user.email },
{
email: req.user.email,
clientPublicKey: clientPublicKey,
serverBInt: bigintConversion.bigintToBuf(server.bInt)
},
{ upsert: true, returnNewDocument: false }
);
return res.status(200).send({
serverPublicKey,
@ -140,8 +136,7 @@ export const srp1 = async (req: Request, res: Response) => {
});
}
);
}
};
/**
* Change account SRP authentication information for user
@ -152,8 +147,8 @@ export const srp1 = async (req: Request, res: Response) => {
* @returns
*/
export const changePassword = async (req: Request, res: Response) => {
const {
clientProof,
const {
clientProof,
protectedKey,
protectedKeyIV,
protectedKeyTag,
@ -166,14 +161,18 @@ export const changePassword = async (req: Request, res: Response) => {
const user = await User.findOne({
email: req.user.email
}).select('+salt +verifier');
}).select("+salt +verifier");
if (!user) throw new Error('Failed to find user');
if (!user) throw new Error("Failed to find user");
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email })
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email });
if (!loginSRPDetailFromDB) {
return BadRequestError(Error("It looks like some details from the first login are not found. Please try login one again"))
return BadRequestError(
Error(
"It looks like some details from the first login are not found. Please try login one again"
)
);
}
const server = new jsrp.server();
@ -207,27 +206,31 @@ export const changePassword = async (req: Request, res: Response) => {
new: true
}
);
if (req.authData.authMode === AUTH_MODE_JWT && req.authData.authPayload instanceof User && req.authData.tokenVersionId) {
await clearTokens(req.authData.tokenVersionId)
if (
req.authData.authMode === AUTH_MODE_JWT &&
req.authData.authPayload instanceof User &&
req.authData.tokenVersionId
) {
await clearTokens(req.authData.tokenVersionId);
}
// clear httpOnly cookie
res.cookie('jid', '', {
res.cookie("jid", "", {
httpOnly: true,
path: '/',
sameSite: 'strict',
path: "/",
sameSite: "strict",
secure: (await getHttpsEnabled()) as boolean
});
return res.status(200).send({
message: 'Successfully changed password'
message: "Successfully changed password"
});
}
return res.status(400).send({
error: 'Failed to change password. Try again?'
error: "Failed to change password. Try again?"
});
}
);
@ -240,22 +243,25 @@ export const changePassword = async (req: Request, res: Response) => {
* @returns
*/
export const createBackupPrivateKey = async (req: Request, res: Response) => {
// create/change backup private key
// requires verifying [clientProof] as part of second step of SRP protocol
// as initiated in /srp1
// create/change backup private key
// requires verifying [clientProof] as part of second step of SRP protocol
// as initiated in /srp1
const { clientProof, encryptedPrivateKey, iv, tag, salt, verifier } =
req.body;
const { clientProof, encryptedPrivateKey, iv, tag, salt, verifier } = req.body;
const user = await User.findOne({
email: req.user.email
}).select('+salt +verifier');
}).select("+salt +verifier");
if (!user) throw new Error('Failed to find user');
if (!user) throw new Error("Failed to find user");
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email })
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email });
if (!loginSRPDetailFromDB) {
return BadRequestError(Error("It looks like some details from the first login are not found. Please try login one again"))
return BadRequestError(
Error(
"It looks like some details from the first login are not found. Please try login one again"
)
);
}
const server = new jsrp.server();
@ -266,9 +272,7 @@ export const createBackupPrivateKey = async (req: Request, res: Response) => {
b: loginSRPDetailFromDB.serverBInt
},
async () => {
server.setClientPublicKey(
loginSRPDetailFromDB.clientPublicKey
);
server.setClientPublicKey(loginSRPDetailFromDB.clientPublicKey);
// compare server and client shared keys
if (server.checkClientProof(clientProof)) {
@ -285,17 +289,17 @@ export const createBackupPrivateKey = async (req: Request, res: Response) => {
verifier
},
{ upsert: true, new: true }
).select('+user, encryptedPrivateKey');
).select("+user, encryptedPrivateKey");
// issue tokens
return res.status(200).send({
message: 'Successfully updated backup private key',
message: "Successfully updated backup private key",
backupPrivateKey
});
}
return res.status(400).send({
message: 'Failed to update backup private key'
message: "Failed to update backup private key"
});
}
);
@ -303,21 +307,21 @@ export const createBackupPrivateKey = async (req: Request, res: Response) => {
/**
* Return backup private key for user
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const getBackupPrivateKey = async (req: Request, res: Response) => {
const backupPrivateKey = await BackupPrivateKey.findOne({
user: req.user._id
}).select('+encryptedPrivateKey +iv +tag');
}).select("+encryptedPrivateKey +iv +tag");
if (!backupPrivateKey) throw new Error('Failed to find backup private key');
if (!backupPrivateKey) throw new Error("Failed to find backup private key");
return res.status(200).send({
backupPrivateKey
});
}
return res.status(200).send({
backupPrivateKey
});
};
export const resetPassword = async (req: Request, res: Response) => {
const {
@ -328,7 +332,7 @@ export const resetPassword = async (req: Request, res: Response) => {
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier,
verifier
} = req.body;
await User.findByIdAndUpdate(
@ -337,7 +341,7 @@ export const resetPassword = async (req: Request, res: Response) => {
encryptionVersion: 2,
protectedKey,
protectedKeyIV,
protectedKeyTag,
protectedKeyTag,
encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag,
@ -349,7 +353,7 @@ export const resetPassword = async (req: Request, res: Response) => {
}
);
return res.status(200).send({
message: 'Successfully reset password'
});
}
return res.status(200).send({
message: "Successfully reset password"
});
};

@ -1,30 +1,30 @@
import { Request, Response } from 'express';
import { Types } from 'mongoose';
import { Key, Secret } from '../../models';
import { Request, Response } from "express";
import { Types } from "mongoose";
import { Key } from "../../models";
import {
v1PushSecrets as push,
pullSecrets as pull,
reformatPullSecrets
} from '../../helpers/secret';
import { pushKeys } from '../../helpers/key';
import { eventPushSecrets } from '../../events';
import { EventService } from '../../services';
import { TelemetryService } from '../../services';
pullSecrets as pull,
v1PushSecrets as push,
reformatPullSecrets
} from "../../helpers/secret";
import { pushKeys } from "../../helpers/key";
import { eventPushSecrets } from "../../events";
import { EventService } from "../../services";
import { TelemetryService } from "../../services";
interface PushSecret {
ciphertextKey: string;
ivKey: string;
tagKey: string;
hashKey: string;
ciphertextValue: string;
ivValue: string;
tagValue: string;
hashValue: string;
ciphertextComment: string;
ivComment: string;
tagComment: string;
hashComment: string;
type: 'shared' | 'personal';
ciphertextKey: string;
ivKey: string;
tagKey: string;
hashKey: string;
ciphertextValue: string;
ivValue: string;
tagValue: string;
hashValue: string;
ciphertextComment: string;
ivComment: string;
tagComment: string;
hashComment: string;
type: "shared" | "personal";
}
/**
@ -35,7 +35,7 @@ interface PushSecret {
* @returns
*/
export const pushSecrets = async (req: Request, res: Response) => {
// upload (encrypted) secrets to workspace with id [workspaceId]
// upload (encrypted) secrets to workspace with id [workspaceId]
const postHogClient = await TelemetryService.getPostHogClient();
let { secrets }: { secrets: PushSecret[] } = req.body;
const { keys, environment, channel } = req.body;
@ -44,13 +44,11 @@ export const pushSecrets = async (req: Request, res: Response) => {
// validate environment
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error('Failed to validate environment');
throw new Error("Failed to validate environment");
}
// sanitize secrets
secrets = secrets.filter(
(s: PushSecret) => s.ciphertextKey !== '' && s.ciphertextValue !== ''
);
secrets = secrets.filter((s: PushSecret) => s.ciphertextKey !== "" && s.ciphertextValue !== "");
await push({
userId: req.user._id,
@ -64,17 +62,16 @@ export const pushSecrets = async (req: Request, res: Response) => {
workspaceId,
keys
});
if (postHogClient) {
postHogClient.capture({
event: 'secrets pushed',
event: "secrets pushed",
distinctId: req.user.email,
properties: {
numberOfSecrets: secrets.length,
environment,
workspaceId,
channel: channel ? channel : 'cli'
channel: channel ? channel : "cli"
}
});
}
@ -87,9 +84,9 @@ export const pushSecrets = async (req: Request, res: Response) => {
})
});
return res.status(200).send({
message: 'Successfully uploaded workspace secrets'
});
return res.status(200).send({
message: "Successfully uploaded workspace secrets"
});
};
/**
@ -100,57 +97,56 @@ export const pushSecrets = async (req: Request, res: Response) => {
* @returns
*/
export const pullSecrets = async (req: Request, res: Response) => {
let secrets;
let key;
let secrets;
const postHogClient = await TelemetryService.getPostHogClient();
const environment: string = req.query.environment as string;
const channel: string = req.query.channel as string;
const { workspaceId } = req.params;
const postHogClient = await TelemetryService.getPostHogClient();
const environment: string = req.query.environment as string;
const channel: string = req.query.channel as string;
const { workspaceId } = req.params;
// validate environment
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error('Failed to validate environment');
}
// validate environment
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error("Failed to validate environment");
}
secrets = await pull({
userId: req.user._id.toString(),
workspaceId,
environment,
channel: channel ? channel : 'cli',
ipAddress: req.realIP
});
secrets = await pull({
userId: req.user._id.toString(),
workspaceId,
environment,
channel: channel ? channel : "cli",
ipAddress: req.realIP
});
key = await Key.findOne({
workspace: workspaceId,
receiver: req.user._id
})
.sort({ createdAt: -1 })
.populate('sender', '+publicKey');
if (channel !== 'cli') {
secrets = reformatPullSecrets({ secrets });
}
const key = await Key.findOne({
workspace: workspaceId,
receiver: req.user._id
})
.sort({ createdAt: -1 })
.populate("sender", "+publicKey");
if (postHogClient) {
// capture secrets pushed event in production
postHogClient.capture({
distinctId: req.user.email,
event: 'secrets pulled',
properties: {
numberOfSecrets: secrets.length,
environment,
workspaceId,
channel: channel ? channel : 'cli'
}
});
}
if (channel !== "cli") {
secrets = reformatPullSecrets({ secrets });
}
return res.status(200).send({
secrets,
key
});
if (postHogClient) {
// capture secrets pushed event in production
postHogClient.capture({
distinctId: req.user.email,
event: "secrets pulled",
properties: {
numberOfSecrets: secrets.length,
environment,
workspaceId,
channel: channel ? channel : "cli"
}
});
}
return res.status(200).send({
secrets,
key
});
};
/**
@ -162,54 +158,51 @@ export const pullSecrets = async (req: Request, res: Response) => {
* @returns
*/
export const pullSecretsServiceToken = async (req: Request, res: Response) => {
let secrets;
let key;
const postHogClient = await TelemetryService.getPostHogClient();
const environment: string = req.query.environment as string;
const channel: string = req.query.channel as string;
const { workspaceId } = req.params;
const postHogClient = await TelemetryService.getPostHogClient();
const environment: string = req.query.environment as string;
const channel: string = req.query.channel as string;
const { workspaceId } = req.params;
// validate environment
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error("Failed to validate environment");
}
// validate environment
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error('Failed to validate environment');
}
const secrets = await pull({
userId: req.serviceToken.user._id.toString(),
workspaceId,
environment,
channel: "cli",
ipAddress: req.realIP
});
secrets = await pull({
userId: req.serviceToken.user._id.toString(),
workspaceId,
environment,
channel: 'cli',
ipAddress: req.realIP
});
const key = {
encryptedKey: req.serviceToken.encryptedKey,
nonce: req.serviceToken.nonce,
sender: {
publicKey: req.serviceToken.publicKey
},
receiver: req.serviceToken.user,
workspace: req.serviceToken.workspace
};
key = {
encryptedKey: req.serviceToken.encryptedKey,
nonce: req.serviceToken.nonce,
sender: {
publicKey: req.serviceToken.publicKey
},
receiver: req.serviceToken.user,
workspace: req.serviceToken.workspace
};
if (postHogClient) {
// capture secrets pulled event in production
postHogClient.capture({
distinctId: req.serviceToken.user.email,
event: "secrets pulled",
properties: {
numberOfSecrets: secrets.length,
environment,
workspaceId,
channel: channel ? channel : "cli"
}
});
}
if (postHogClient) {
// capture secrets pulled event in production
postHogClient.capture({
distinctId: req.serviceToken.user.email,
event: 'secrets pulled',
properties: {
numberOfSecrets: secrets.length,
environment,
workspaceId,
channel: channel ? channel : 'cli'
}
});
}
return res.status(200).send({
secrets: reformatPullSecrets({ secrets }),
key
});
return res.status(200).send({
secrets: reformatPullSecrets({ secrets }),
key
});
};

@ -5,13 +5,13 @@ import { BadRequestError } from "../../utils/errors";
import {
appendFolder,
deleteFolderById,
getAllFolderIds,
searchByFolderIdWithDir,
searchByFolderId,
validateFolderName,
generateFolderId,
getParentFromFolderId,
getAllFolderIds,
getFolderByPath,
getParentFromFolderId,
searchByFolderId,
searchByFolderIdWithDir,
validateFolderName,
} from "../../services/FolderService";
import { ADMIN, MEMBER } from "../../variables";
import { validateMembership } from "../../helpers/membership";

@ -1,7 +1,7 @@
import { Request, Response } from 'express';
import { ServiceToken } from '../../models';
import { createToken } from '../../helpers/auth';
import { getJwtServiceSecret } from '../../config';
import { Request, Response } from "express";
import { ServiceToken } from "../../models";
import { createToken } from "../../helpers/auth";
import { getJwtServiceSecret } from "../../config";
/**
* Return service token on request
@ -11,7 +11,7 @@ import { getJwtServiceSecret } from '../../config';
*/
export const getServiceToken = async (req: Request, res: Response) => {
return res.status(200).send({
serviceToken: req.serviceToken
serviceToken: req.serviceToken,
});
};
@ -31,13 +31,13 @@ export const createServiceToken = async (req: Request, res: Response) => {
expiresIn,
publicKey,
encryptedKey,
nonce
nonce,
} = req.body;
// validate environment
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error('Failed to validate environment');
throw new Error("Failed to validate environment");
}
// compute access token expiration date
@ -52,24 +52,24 @@ export const createServiceToken = async (req: Request, res: Response) => {
expiresAt,
publicKey,
encryptedKey,
nonce
nonce,
}).save();
token = createToken({
payload: {
serviceTokenId: serviceToken._id.toString(),
workspaceId
workspaceId,
},
expiresIn: expiresIn,
secret: await getJwtServiceSecret()
secret: await getJwtServiceSecret(),
});
} catch (err) {
return res.status(400).send({
message: 'Failed to create service token'
message: "Failed to create service token",
});
}
return res.status(200).send({
token
token,
});
};

@ -1,13 +1,15 @@
import { Request, Response } from 'express';
import { User } from '../../models';
import { Request, Response } from "express";
import { User } from "../../models";
import { checkEmailVerification, sendEmailVerification } from "../../helpers/signup";
import { createToken } from "../../helpers/auth";
import { BadRequestError } from "../../utils/errors";
import {
sendEmailVerification,
checkEmailVerification,
} from '../../helpers/signup';
import { createToken } from '../../helpers/auth';
import { BadRequestError } from '../../utils/errors';
import { getInviteOnlySignup, getJwtSignupLifetime, getJwtSignupSecret, getSmtpConfigured } from '../../config';
import { validateUserEmail } from '../../validation';
getInviteOnlySignup,
getJwtSignupLifetime,
getJwtSignupSecret,
getSmtpConfigured
} from "../../config";
import { validateUserEmail } from "../../validation";
/**
* Signup step 1: Initialize account for user under email [email] and send a verification code
@ -17,27 +19,26 @@ import { validateUserEmail } from '../../validation';
* @returns
*/
export const beginEmailSignup = async (req: Request, res: Response) => {
let email: string;
email = req.body.email;
const email: string = req.body.email;
// validate that email is not disposable
validateUserEmail(email);
const user = await User.findOne({ email }).select('+publicKey');
const user = await User.findOne({ email }).select("+publicKey");
if (user && user?.publicKey) {
// case: user has already completed account
return res.status(403).send({
error: 'Failed to send email verification code for complete account'
error: "Failed to send email verification code for complete account"
});
}
// send send verification email
await sendEmailVerification({ email });
return res.status(200).send({
message: `Sent an email verification code to ${email}`
});
return res.status(200).send({
message: `Sent an email verification code to ${email}`
});
};
/**
@ -48,23 +49,25 @@ export const beginEmailSignup = async (req: Request, res: Response) => {
* @returns
*/
export const verifyEmailSignup = async (req: Request, res: Response) => {
let user, token;
let user;
const { email, code } = req.body;
// initialize user account
user = await User.findOne({ email }).select('+publicKey');
user = await User.findOne({ email }).select("+publicKey");
if (user && user?.publicKey) {
// case: user has already completed account
return res.status(403).send({
error: 'Failed email verification for complete user'
error: "Failed email verification for complete user"
});
}
if (await getInviteOnlySignup()) {
// 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) {
throw BadRequestError({ message: "New user sign ups are not allowed at this time. You must be invited to sign up." })
throw BadRequestError({
message: "New user sign ups are not allowed at this time. You must be invited to sign up."
});
}
}
@ -83,7 +86,7 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
}
// generate temporary signup token
token = createToken({
const token = createToken({
payload: {
userId: user._id.toString()
},
@ -91,9 +94,9 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
secret: await getJwtSignupSecret()
});
return res.status(200).send({
message: 'Successfuly verified email',
user,
token
});
return res.status(200).send({
message: "Successfuly verified email",
user,
token
});
};

@ -1,6 +1,6 @@
import { Request, Response } from 'express';
import Stripe from 'stripe';
import { getStripeSecretKey, getStripeWebhookSecret } from '../../config';
import { Request, Response } from "express";
import Stripe from "stripe";
import { getStripeSecretKey, getStripeWebhookSecret } from "../../config";
/**
* Handle service provisioning/un-provisioning via Stripe
@ -11,10 +11,10 @@ import { getStripeSecretKey, getStripeWebhookSecret } from '../../config';
export const handleWebhook = async (req: Request, res: Response) => {
// check request for valid stripe signature
const stripe = new Stripe(await getStripeSecretKey(), {
apiVersion: '2022-08-01'
apiVersion: "2022-08-01",
});
const sig = req.headers['stripe-signature'] as string;
const sig = req.headers["stripe-signature"] as string;
const event = stripe.webhooks.constructEvent(
req.body,
sig,
@ -22,7 +22,7 @@ export const handleWebhook = async (req: Request, res: Response) => {
);
switch (event.type) {
case '':
case "":
break;
default:
}

@ -1,5 +1,5 @@
import { Request, Response } from 'express';
import { UserAction } from '../../models';
import { Request, Response } from "express";
import { UserAction } from "../../models";
/**
* Add user action [action]
@ -15,18 +15,18 @@ export const addUserAction = async (req: Request, res: Response) => {
const userAction = await UserAction.findOneAndUpdate(
{
user: req.user._id,
action
action,
},
{ user: req.user._id, action },
{
new: true,
upsert: true
upsert: true,
}
);
return res.status(200).send({
message: 'Successfully recorded user action',
userAction
message: "Successfully recorded user action",
userAction,
});
};
@ -42,10 +42,10 @@ export const getUserAction = async (req: Request, res: Response) => {
const userAction = await UserAction.findOne({
user: req.user._id,
action
action,
});
return res.status(200).send({
userAction
userAction,
});
};

@ -1,4 +1,4 @@
import { Request, Response } from 'express';
import { Request, Response } from "express";
/**
* Return user on request
@ -8,6 +8,6 @@ import { Request, Response } from 'express';
*/
export const getUser = async (req: Request, res: Response) => {
return res.status(200).send({
user: req.user
user: req.user,
});
};

@ -1,19 +1,18 @@
import { Request, Response } from "express";
import {
Workspace,
Membership,
MembershipOrg,
IUser,
Integration,
IntegrationAuth,
IUser,
Membership,
MembershipOrg,
ServiceToken,
ServiceTokenData,
Workspace,
} from "../../models";
import {
createWorkspace as create,
deleteWorkspace as deleteWork,
} from "../../helpers/workspace";
import { EELicenseService } from '../../ee/services';
import { EELicenseService } from "../../ee/services";
import { addMemberships } from "../../helpers/membership";
import { ADMIN } from "../../variables";
@ -123,7 +122,7 @@ export const createWorkspace = async (req: Request, res: Response) => {
if (plan.workspacesUsed >= plan.workspaceLimit) {
// case: number of workspaces used exceeds the number of workspaces allowed
return res.status(400).send({
message: 'Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces.'
message: "Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces.",
});
}
}

@ -1,74 +1,72 @@
import { Request, Response } from 'express';
import crypto from 'crypto';
import bcrypt from 'bcrypt';
import {
APIKeyData
} from '../../models';
import { getSaltRounds } from '../../config';
import { Request, Response } from "express";
import crypto from "crypto";
import bcrypt from "bcrypt";
import { APIKeyData } from "../../models";
import { getSaltRounds } from "../../config";
/**
* Return API key data for user with id [req.user_id]
* @param req
* @param res
* @returns
* @param res
* @returns
*/
export const getAPIKeyData = async (req: Request, res: Response) => {
const apiKeyData = await APIKeyData.find({
user: req.user._id
});
return res.status(200).send({
apiKeyData
});
}
const apiKeyData = await APIKeyData.find({
user: req.user._id,
});
return res.status(200).send({
apiKeyData,
});
};
/**
* Create new API key data for user with id [req.user._id]
* @param req
* @param res
* @param req
* @param res
*/
export const createAPIKeyData = async (req: Request, res: Response) => {
const { name, expiresIn } = req.body;
const secret = crypto.randomBytes(16).toString('hex');
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
const expiresAt = new Date();
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
let apiKeyData = await new APIKeyData({
name,
lastUsed: new Date(),
expiresAt,
user: req.user._id,
secretHash
}).save();
// return api key data without sensitive data
// FIX: fix this any
apiKeyData = await APIKeyData.findById(apiKeyData._id) as any
if (!apiKeyData) throw new Error('Failed to find API key data');
const apiKey = `ak.${apiKeyData._id.toString()}.${secret}`;
return res.status(200).send({
apiKey,
apiKeyData
});
}
const { name, expiresIn } = req.body;
const secret = crypto.randomBytes(16).toString("hex");
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
const expiresAt = new Date();
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
let apiKeyData = await new APIKeyData({
name,
lastUsed: new Date(),
expiresAt,
user: req.user._id,
secretHash,
}).save();
// return api key data without sensitive data
// FIX: fix this any
apiKeyData = (await APIKeyData.findById(apiKeyData._id)) as any;
if (!apiKeyData) throw new Error("Failed to find API key data");
const apiKey = `ak.${apiKeyData._id.toString()}.${secret}`;
return res.status(200).send({
apiKey,
apiKeyData,
});
};
/**
* Delete API key data with id [apiKeyDataId].
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const deleteAPIKeyData = async (req: Request, res: Response) => {
const { apiKeyDataId } = req.params;
const apiKeyData = await APIKeyData.findByIdAndDelete(apiKeyDataId);
return res.status(200).send({
apiKeyData
});
}
const { apiKeyDataId } = req.params;
const apiKeyData = await APIKeyData.findByIdAndDelete(apiKeyDataId);
return res.status(200).send({
apiKeyData,
});
};

@ -1,27 +1,27 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import { Request, Response } from 'express';
import jwt from 'jsonwebtoken';
import * as bigintConversion from 'bigint-conversion';
const jsrp = require('jsrp');
import { User, LoginSRPDetail } from '../../models';
import { issueAuthTokens, createToken } from '../../helpers/auth';
import { checkUserDevice } from '../../helpers/user';
import { sendMail } from '../../helpers/nodemailer';
import { TokenService } from '../../services';
import { EELogService } from '../../ee/services';
import { BadRequestError, InternalServerError } from '../../utils/errors';
import { Request, Response } from "express";
import jwt from "jsonwebtoken";
import * as bigintConversion from "bigint-conversion";
const jsrp = require("jsrp");
import { LoginSRPDetail, User } from "../../models";
import { createToken, issueAuthTokens } from "../../helpers/auth";
import { checkUserDevice } from "../../helpers/user";
import { sendMail } from "../../helpers/nodemailer";
import { TokenService } from "../../services";
import { EELogService } from "../../ee/services";
import { BadRequestError, InternalServerError } from "../../utils/errors";
import {
ACTION_LOGIN,
TOKEN_EMAIL_MFA,
ACTION_LOGIN
} from '../../variables';
import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this
} from "../../variables";
import { getChannelFromUserAgent } from "../../utils/posthog"; // TODO: move this
import {
getHttpsEnabled,
getJwtMfaLifetime,
getJwtMfaSecret,
getHttpsEnabled
} from '../../config';
} from "../../config";
declare module 'jsonwebtoken' {
declare module "jsonwebtoken" {
export interface UserIDJwtPayload extends jwt.JwtPayload {
userId: string;
}
@ -36,20 +36,20 @@ declare module 'jsonwebtoken' {
export const login1 = async (req: Request, res: Response) => {
const {
email,
clientPublicKey
clientPublicKey,
}: { email: string; clientPublicKey: string } = req.body;
const user = await User.findOne({
email
}).select('+salt +verifier');
email,
}).select("+salt +verifier");
if (!user) throw new Error('Failed to find user');
if (!user) throw new Error("Failed to find user");
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier
verifier: user.verifier,
},
async () => {
// generate server-side public key
@ -63,7 +63,7 @@ export const login1 = async (req: Request, res: Response) => {
return res.status(200).send({
serverPublicKey,
salt: user.salt
salt: user.salt,
});
}
);
@ -78,14 +78,14 @@ export const login1 = async (req: Request, res: Response) => {
* @returns
*/
export const login2 = async (req: Request, res: Response) => {
if (!req.headers['user-agent']) throw InternalServerError({ message: 'User-Agent header is required' });
if (!req.headers["user-agent"]) throw InternalServerError({ message: "User-Agent header is required" });
const { email, clientProof } = req.body;
const user = await User.findOne({
email
}).select('+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices');
email,
}).select("+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices");
if (!user) throw new Error('Failed to find user');
if (!user) throw new Error("Failed to find user");
const loginSRPDetail = await LoginSRPDetail.findOneAndDelete({ email: email })
@ -98,7 +98,7 @@ export const login2 = async (req: Request, res: Response) => {
{
salt: user.salt,
verifier: user.verifier,
b: loginSRPDetail.serverBInt
b: loginSRPDetail.serverBInt,
},
async () => {
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
@ -111,52 +111,52 @@ export const login2 = async (req: Request, res: Response) => {
// generate temporary MFA token
const token = createToken({
payload: {
userId: user._id.toString()
userId: user._id.toString(),
},
expiresIn: await getJwtMfaLifetime(),
secret: await getJwtMfaSecret()
secret: await getJwtMfaSecret(),
});
const code = await TokenService.createToken({
type: TOKEN_EMAIL_MFA,
email
email,
});
// send MFA code [code] to [email]
await sendMail({
template: 'emailMfa.handlebars',
subjectLine: 'Infisical MFA code',
template: "emailMfa.handlebars",
subjectLine: "Infisical MFA code",
recipients: [email],
substitutions: {
code
}
code,
},
});
return res.status(200).send({
mfaEnabled: true,
token
token,
});
}
await checkUserDevice({
user,
ip: req.realIP,
userAgent: req.headers['user-agent'] ?? ''
userAgent: req.headers["user-agent"] ?? "",
});
// issue tokens
const tokens = await issueAuthTokens({
userId: user._id,
ip: req.realIP,
userAgent: req.headers['user-agent'] ?? ''
userAgent: req.headers["user-agent"] ?? "",
});
// store (refresh) token in httpOnly cookie
res.cookie('jid', tokens.refreshToken, {
res.cookie("jid", tokens.refreshToken, {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: await getHttpsEnabled()
path: "/",
sameSite: "strict",
secure: await getHttpsEnabled(),
});
// case: user does not have MFA enabled
@ -182,7 +182,7 @@ export const login2 = async (req: Request, res: Response) => {
publicKey: user.publicKey,
encryptedPrivateKey: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag
tag: user.tag,
}
if (
@ -197,21 +197,21 @@ export const login2 = async (req: Request, res: Response) => {
const loginAction = await EELogService.createAction({
name: ACTION_LOGIN,
userId: user._id
userId: user._id,
});
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers['user-agent']),
ipAddress: req.ip
channel: getChannelFromUserAgent(req.headers["user-agent"]),
ipAddress: req.ip,
});
return res.status(200).send(response);
}
return res.status(400).send({
message: 'Failed to authenticate. Try again?'
message: "Failed to authenticate. Try again?",
});
}
);
@ -227,21 +227,21 @@ export const sendMfaToken = async (req: Request, res: Response) => {
const code = await TokenService.createToken({
type: TOKEN_EMAIL_MFA,
email
email,
});
// send MFA code [code] to [email]
await sendMail({
template: 'emailMfa.handlebars',
subjectLine: 'Infisical MFA code',
template: "emailMfa.handlebars",
subjectLine: "Infisical MFA code",
recipients: [email],
substitutions: {
code
}
code,
},
});
return res.status(200).send({
message: 'Successfully sent new MFA code'
message: "Successfully sent new MFA code",
});
}
@ -257,36 +257,36 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
await TokenService.validateToken({
type: TOKEN_EMAIL_MFA,
email,
token: mfaToken
token: mfaToken,
});
const user = await User.findOne({
email
}).select('+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices');
email,
}).select("+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices");
if (!user) throw new Error('Failed to find user');
if (!user) throw new Error("Failed to find user");
await LoginSRPDetail.deleteOne({ userId: user.id })
await checkUserDevice({
user,
ip: req.realIP,
userAgent: req.headers['user-agent'] ?? ''
userAgent: req.headers["user-agent"] ?? "",
});
// issue tokens
const tokens = await issueAuthTokens({
userId: user._id,
ip: req.realIP,
userAgent: req.headers['user-agent'] ?? ''
userAgent: req.headers["user-agent"] ?? "",
});
// store (refresh) token in httpOnly cookie
res.cookie('jid', tokens.refreshToken, {
res.cookie("jid", tokens.refreshToken, {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: await getHttpsEnabled()
path: "/",
sameSite: "strict",
secure: await getHttpsEnabled(),
});
interface VerifyMfaTokenRes {
@ -319,7 +319,7 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
publicKey: user.publicKey as string,
encryptedPrivateKey: user.encryptedPrivateKey as string,
iv: user.iv as string,
tag: user.tag as string
tag: user.tag as string,
}
if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) {
@ -330,14 +330,14 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
const loginAction = await EELogService.createAction({
name: ACTION_LOGIN,
userId: user._id
userId: user._id,
});
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers['user-agent']),
ipAddress: req.realIP
channel: getChannelFromUserAgent(req.headers["user-agent"]),
ipAddress: req.realIP,
});
return res.status(200).send(resObj);

@ -1,17 +1,17 @@
import { Request, Response } from 'express';
import { Request, Response } from "express";
import {
Integration,
Membership,
Secret,
ServiceToken,
Workspace,
Integration,
ServiceTokenData,
Membership,
} from '../../models';
import { SecretVersion } from '../../ee/models';
import { EELicenseService } from '../../ee/services';
import { BadRequestError, WorkspaceNotFoundError } from '../../utils/errors';
import _ from 'lodash';
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from '../../variables';
Workspace,
} from "../../models";
import { SecretVersion } from "../../ee/models";
import { EELicenseService } from "../../ee/services";
import { BadRequestError, WorkspaceNotFoundError } from "../../utils/errors";
import _ from "lodash";
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables";
/**
* Create new workspace environment named [environmentName] under workspace with id
@ -38,7 +38,7 @@ export const createWorkspaceEnvironment = async (
// case: number of environments used exceeds the number of environments allowed
return res.status(400).send({
message: 'Failed to create environment due to environment limit reached. Upgrade plan to create more environments.'
message: "Failed to create environment due to environment limit reached. Upgrade plan to create more environments.",
});
}
}
@ -49,7 +49,7 @@ export const createWorkspaceEnvironment = async (
({ name, slug }) => slug === environmentSlug || environmentName === name
)
) {
throw new Error('Failed to create workspace environment');
throw new Error("Failed to create workspace environment");
}
workspace?.environments.push({
@ -61,7 +61,7 @@ export const createWorkspaceEnvironment = async (
await EELicenseService.refreshPlan(workspace.organization.toString(), workspaceId);
return res.status(200).send({
message: 'Successfully created new environment',
message: "Successfully created new environment",
workspace: workspaceId,
environment: {
name: environmentName,
@ -85,13 +85,13 @@ export const renameWorkspaceEnvironment = async (
const { environmentName, environmentSlug, oldEnvironmentSlug } = req.body;
// user should pass both new slug and env name
if (!environmentSlug || !environmentName) {
throw new Error('Invalid environment given.');
throw new Error("Invalid environment given.");
}
// atomic update the env to avoid conflict
const workspace = await Workspace.findById(workspaceId).exec();
if (!workspace) {
throw new Error('Failed to create workspace environment');
throw new Error("Failed to create workspace environment");
}
const isEnvExist = workspace.environments.some(
@ -100,14 +100,14 @@ export const renameWorkspaceEnvironment = async (
(name === environmentName || slug === environmentSlug)
);
if (isEnvExist) {
throw new Error('Invalid environment given');
throw new Error("Invalid environment given");
}
const envIndex = workspace?.environments.findIndex(
({ slug }) => slug === oldEnvironmentSlug
);
if (envIndex === -1) {
throw new Error('Invalid environment given');
throw new Error("Invalid environment given");
}
workspace.environments[envIndex].name = environmentName;
@ -137,7 +137,7 @@ export const renameWorkspaceEnvironment = async (
await Membership.updateMany(
{
workspace: workspaceId,
"deniedPermissions.environmentSlug": oldEnvironmentSlug
"deniedPermissions.environmentSlug": oldEnvironmentSlug,
},
{ $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } },
{ arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] }
@ -145,7 +145,7 @@ export const renameWorkspaceEnvironment = async (
return res.status(200).send({
message: 'Successfully update environment',
message: "Successfully update environment",
workspace: workspaceId,
environment: {
name: environmentName,
@ -169,14 +169,14 @@ export const deleteWorkspaceEnvironment = async (
// atomic update the env to avoid conflict
const workspace = await Workspace.findById(workspaceId).exec();
if (!workspace) {
throw new Error('Failed to create workspace environment');
throw new Error("Failed to create workspace environment");
}
const envIndex = workspace?.environments.findIndex(
({ slug }) => slug === environmentSlug
);
if (envIndex === -1) {
throw new Error('Invalid environment given');
throw new Error("Invalid environment given");
}
workspace.environments.splice(envIndex, 1);
@ -211,7 +211,7 @@ export const deleteWorkspaceEnvironment = async (
await EELicenseService.refreshPlan(workspace.organization.toString(), workspaceId);
return res.status(200).send({
message: 'Successfully deleted environment',
message: "Successfully deleted environment",
workspace: workspaceId,
environment: environmentSlug,
});
@ -225,7 +225,7 @@ export const getAllAccessibleEnvironmentsOfWorkspace = async (
const { workspaceId } = req.params;
const workspacesUserIsMemberOf = await Membership.findOne({
workspace: workspaceId,
user: req.user
user: req.user,
})
if (!workspacesUserIsMemberOf) {
@ -249,7 +249,7 @@ export const getAllAccessibleEnvironmentsOfWorkspace = async (
name: environment.name,
slug: environment.slug,
isWriteDenied: isWriteBlocked,
isReadDenied: isReadBlocked
isReadDenied: isReadBlocked,
})
}
})

@ -1,15 +1,15 @@
import * as authController from './authController';
import * as signupController from './signupController';
import * as usersController from './usersController';
import * as organizationsController from './organizationsController';
import * as workspaceController from './workspaceController';
import * as serviceTokenDataController from './serviceTokenDataController';
import * as apiKeyDataController from './apiKeyDataController';
import * as secretController from './secretController';
import * as secretsController from './secretsController';
import * as serviceAccountsController from './serviceAccountsController';
import * as environmentController from './environmentController';
import * as tagController from './tagController';
import * as authController from "./authController";
import * as signupController from "./signupController";
import * as usersController from "./usersController";
import * as organizationsController from "./organizationsController";
import * as workspaceController from "./workspaceController";
import * as serviceTokenDataController from "./serviceTokenDataController";
import * as apiKeyDataController from "./apiKeyDataController";
import * as secretController from "./secretController";
import * as secretsController from "./secretsController";
import * as serviceAccountsController from "./serviceAccountsController";
import * as environmentController from "./environmentController";
import * as tagController from "./tagController";
export {
authController,
@ -23,5 +23,5 @@ export {
secretsController,
serviceAccountsController,
environmentController,
tagController
tagController,
}

@ -1,13 +1,13 @@
import { Request, Response } from 'express';
import { Types } from 'mongoose';
import { Request, Response } from "express";
import { Types } from "mongoose";
import {
MembershipOrg,
Membership,
MembershipOrg,
ServiceAccount,
Workspace,
ServiceAccount
} from '../../models';
import { deleteMembershipOrg } from '../../helpers/membershipOrg';
import { updateSubscriptionOrgQuantity } from '../../helpers/organization';
} from "../../models";
import { deleteMembershipOrg } from "../../helpers/membershipOrg";
import { updateSubscriptionOrgQuantity } from "../../helpers/organization";
/**
* Return memberships for organization with id [organizationId]
@ -51,11 +51,11 @@ export const getOrganizationMemberships = async (req: Request, res: Response) =>
const { organizationId } = req.params;
const memberships = await MembershipOrg.find({
organization: organizationId
}).populate('user', '+publicKey');
organization: organizationId,
}).populate("user", "+publicKey");
return res.status(200).send({
memberships
memberships,
});
}
@ -124,14 +124,14 @@ export const updateOrganizationMembership = async (req: Request, res: Response)
const membership = await MembershipOrg.findByIdAndUpdate(
membershipId,
{
role
role,
}, {
new: true
new: true,
}
);
return res.status(200).send({
membership
membership,
});
}
@ -182,15 +182,15 @@ export const deleteOrganizationMembership = async (req: Request, res: Response)
// delete organization membership
const membership = await deleteMembershipOrg({
membershipOrgId: membershipId
membershipOrgId: membershipId,
});
await updateSubscriptionOrgQuantity({
organizationId: membership.organization.toString()
organizationId: membership.organization.toString(),
});
return res.status(200).send({
membership
membership,
});
}
@ -240,23 +240,23 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) =>
(
await Workspace.find(
{
organization: organizationId
organization: organizationId,
},
'_id'
"_id"
)
).map((w) => w._id.toString())
);
const workspaces = (
await Membership.find({
user: req.user._id
}).populate('workspace')
user: req.user._id,
}).populate("workspace")
)
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
.map((m) => m.workspace);
return res.status(200).send({
workspaces
workspaces,
});
}
@ -269,10 +269,10 @@ export const getOrganizationServiceAccounts = async (req: Request, res: Response
const { organizationId } = req.params;
const serviceAccounts = await ServiceAccount.find({
organization: new Types.ObjectId(organizationId)
organization: new Types.ObjectId(organizationId),
});
return res.status(200).send({
serviceAccounts
serviceAccounts,
});
}

@ -2,24 +2,39 @@ import to from "await-to-js";
import { Request, Response } from "express";
import mongoose, { Types } from "mongoose";
import Secret, { ISecret } from "../../models/secret";
import { CreateSecretRequestBody, ModifySecretRequestBody, SanitizedSecretForCreate, SanitizedSecretModify } from "../../types/secret";
import {
CreateSecretRequestBody,
ModifySecretRequestBody,
SanitizedSecretForCreate,
SanitizedSecretModify
} from "../../types/secret";
const { ValidationError } = mongoose.Error;
import { BadRequestError, InternalServerError, UnauthorizedRequestError, ValidationError as RouteValidationError } from '../../utils/errors';
import { AnyBulkWriteOperation } from 'mongodb';
import { ALGORITHM_AES_256_GCM, ENCODING_SCHEME_UTF8, SECRET_PERSONAL, SECRET_SHARED } from "../../variables";
import { TelemetryService } from '../../services';
import {
BadRequestError,
InternalServerError,
ValidationError as RouteValidationError,
UnauthorizedRequestError
} from "../../utils/errors";
import { AnyBulkWriteOperation } from "mongodb";
import {
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
SECRET_PERSONAL,
SECRET_SHARED
} from "../../variables";
import { TelemetryService } from "../../services";
import { User } from "../../models";
import { AccountNotFoundError } from '../../utils/errors';
import { AccountNotFoundError } from "../../utils/errors";
/**
* Create secret for workspace with id [workspaceId] and environment [environment]
* @param req
* @param res
* @param req
* @param res
*/
export const createSecret = async (req: Request, res: Response) => {
const postHogClient = await TelemetryService.getPostHogClient();
const secretToCreate: CreateSecretRequestBody = req.body.secret;
const { workspaceId, environment } = req.params
const { workspaceId, environment } = req.params;
const sanitizedSecret: SanitizedSecretForCreate = {
secretKeyCiphertext: secretToCreate.secretKeyCiphertext,
secretKeyIV: secretToCreate.secretKeyIV,
@ -39,45 +54,44 @@ export const createSecret = async (req: Request, res: Response) => {
user: new Types.ObjectId(req.user._id),
algorithm: ALGORITHM_AES_256_GCM,
keyEncoding: ENCODING_SCHEME_UTF8
}
};
const [error, secret] = await to(Secret.create(sanitizedSecret).then())
const [error, secret] = await to(Secret.create(sanitizedSecret).then());
if (error instanceof ValidationError) {
throw RouteValidationError({ message: error.message, stack: error.stack })
throw RouteValidationError({ message: error.message, stack: error.stack });
}
if (postHogClient) {
postHogClient.capture({
event: 'secrets added',
event: "secrets added",
distinctId: req.user.email,
properties: {
numberOfSecrets: 1,
workspaceId,
environment,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
userAgent: req.headers?.["user-agent"]
}
});
}
res.status(200).send({
secret
})
}
});
};
/**
* Create many secrets for workspace wiht id [workspaceId] and environment [environment]
* @param req
* @param res
* @param req
* @param res
*/
export const createSecrets = async (req: Request, res: Response) => {
const postHogClient = await TelemetryService.getPostHogClient();
const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets;
const { workspaceId, environment } = req.params
const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = []
const { workspaceId, environment } = req.params;
const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = [];
secretsToCreate.forEach(rawSecret => {
secretsToCreate.forEach((rawSecret) => {
const safeUpdateFields: SanitizedSecretForCreate = {
secretKeyCiphertext: rawSecret.secretKeyCiphertext,
secretKeyIV: rawSecret.secretKeyIV,
@ -97,140 +111,163 @@ export const createSecrets = async (req: Request, res: Response) => {
user: new Types.ObjectId(req.user._id),
algorithm: ALGORITHM_AES_256_GCM,
keyEncoding: ENCODING_SCHEME_UTF8
}
};
sanitizedSecretesToCreate.push(safeUpdateFields)
})
sanitizedSecretesToCreate.push(safeUpdateFields);
});
const [bulkCreateError, secrets] = await to(Secret.insertMany(sanitizedSecretesToCreate).then())
const [bulkCreateError, secrets] = await to(Secret.insertMany(sanitizedSecretesToCreate).then());
if (bulkCreateError) {
if (bulkCreateError instanceof ValidationError) {
throw RouteValidationError({ message: bulkCreateError.message, stack: bulkCreateError.stack })
throw RouteValidationError({
message: bulkCreateError.message,
stack: bulkCreateError.stack
});
}
throw InternalServerError({ message: "Unable to process your batch create request. Please try again", stack: bulkCreateError.stack })
throw InternalServerError({
message: "Unable to process your batch create request. Please try again",
stack: bulkCreateError.stack
});
}
if (postHogClient) {
postHogClient.capture({
event: 'secrets added',
event: "secrets added",
distinctId: req.user.email,
properties: {
numberOfSecrets: (secretsToCreate ?? []).length,
workspaceId,
environment,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
userAgent: req.headers?.["user-agent"]
}
});
}
res.status(200).send({
secrets
})
}
});
};
/**
* Delete secrets in workspace with id [workspaceId] and environment [environment]
* @param req
* @param res
* @param req
* @param res
*/
export const deleteSecrets = async (req: Request, res: Response) => {
const postHogClient = await TelemetryService.getPostHogClient();
const { workspaceId, environmentName } = req.params
const secretIdsToDelete: string[] = req.body.secretIds
const { workspaceId, environmentName } = req.params;
const secretIdsToDelete: string[] = req.body.secretIds;
const [secretIdsUserCanDeleteError, secretIdsUserCanDelete] = await to(Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then())
const [secretIdsUserCanDeleteError, secretIdsUserCanDelete] = await to(
Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then()
);
if (secretIdsUserCanDeleteError) {
throw InternalServerError({ message: `Unable to fetch secrets you own: [error=${secretIdsUserCanDeleteError.message}]` })
throw InternalServerError({
message: `Unable to fetch secrets you own: [error=${secretIdsUserCanDeleteError.message}]`
});
}
const secretsUserCanDeleteSet: Set<string> = new Set(secretIdsUserCanDelete.map(objectId => objectId._id.toString()));
const deleteOperationsToPerform: AnyBulkWriteOperation<ISecret>[] = []
const secretsUserCanDeleteSet: Set<string> = new Set(
secretIdsUserCanDelete.map((objectId) => objectId._id.toString())
);
const deleteOperationsToPerform: AnyBulkWriteOperation<ISecret>[] = [];
let numSecretsDeleted = 0;
secretIdsToDelete.forEach(secretIdToDelete => {
secretIdsToDelete.forEach((secretIdToDelete) => {
if (secretsUserCanDeleteSet.has(secretIdToDelete)) {
const deleteOperation = { deleteOne: { filter: { _id: new Types.ObjectId(secretIdToDelete) } } }
deleteOperationsToPerform.push(deleteOperation)
const deleteOperation = {
deleteOne: { filter: { _id: new Types.ObjectId(secretIdToDelete) } }
};
deleteOperationsToPerform.push(deleteOperation);
numSecretsDeleted++;
} else {
throw RouteValidationError({ message: "You cannot delete secrets that you do not have access to" })
throw RouteValidationError({
message: "You cannot delete secrets that you do not have access to"
});
}
})
});
const [bulkDeleteError, bulkDelete] = await to(Secret.bulkWrite(deleteOperationsToPerform).then())
const [bulkDeleteError] = await to(Secret.bulkWrite(deleteOperationsToPerform).then());
if (bulkDeleteError) {
if (bulkDeleteError instanceof ValidationError) {
throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: bulkDeleteError.stack })
throw RouteValidationError({
message: "Unable to apply modifications, please try again",
stack: bulkDeleteError.stack
});
}
throw InternalServerError()
throw InternalServerError();
}
if (postHogClient) {
postHogClient.capture({
event: 'secrets deleted',
event: "secrets deleted",
distinctId: req.user.email,
properties: {
numberOfSecrets: numSecretsDeleted,
environment: environmentName,
workspaceId,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
userAgent: req.headers?.["user-agent"]
}
});
}
res.status(200).send()
}
res.status(200).send();
};
/**
* Delete secret with id [secretId]
* @param req
* @param req
* @param res
*/
export const deleteSecret = async (req: Request, res: Response) => {
const postHogClient = await TelemetryService.getPostHogClient();
await Secret.findByIdAndDelete(req._secret._id)
await Secret.findByIdAndDelete(req._secret._id);
if (postHogClient) {
postHogClient.capture({
event: 'secrets deleted',
event: "secrets deleted",
distinctId: req.user.email,
properties: {
numberOfSecrets: 1,
workspaceId: req._secret.workspace.toString(),
environment: req._secret.environment,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
userAgent: req.headers?.["user-agent"]
}
});
}
res.status(200).send({
secret: req._secret
})
}
});
};
/**
* Update secrets for workspace with id [workspaceId] and environment [environment]
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const updateSecrets = async (req: Request, res: Response) => {
const postHogClient = await TelemetryService.getPostHogClient();
const { workspaceId, environmentName } = req.params
const { workspaceId, environmentName } = req.params;
const secretsModificationsRequested: ModifySecretRequestBody[] = req.body.secrets;
const [secretIdsUserCanModifyError, secretIdsUserCanModify] = await to(Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then())
const [secretIdsUserCanModifyError, secretIdsUserCanModify] = await to(
Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then()
);
if (secretIdsUserCanModifyError) {
throw InternalServerError({ message: "Unable to fetch secrets you own" })
throw InternalServerError({ message: "Unable to fetch secrets you own" });
}
const secretsUserCanModifySet: Set<string> = new Set(secretIdsUserCanModify.map(objectId => objectId._id.toString()));
const updateOperationsToPerform: any = []
const secretsUserCanModifySet: Set<string> = new Set(
secretIdsUserCanModify.map((objectId) => objectId._id.toString())
);
const updateOperationsToPerform: any = [];
secretsModificationsRequested.forEach(userModifiedSecret => {
secretsModificationsRequested.forEach((userModifiedSecret) => {
if (secretsUserCanModifySet.has(userModifiedSecret._id.toString())) {
const sanitizedSecret: SanitizedSecretModify = {
secretKeyCiphertext: userModifiedSecret.secretKeyCiphertext,
@ -244,56 +281,70 @@ export const updateSecrets = async (req: Request, res: Response) => {
secretCommentCiphertext: userModifiedSecret.secretCommentCiphertext,
secretCommentIV: userModifiedSecret.secretCommentIV,
secretCommentTag: userModifiedSecret.secretCommentTag,
secretCommentHash: userModifiedSecret.secretCommentHash,
}
secretCommentHash: userModifiedSecret.secretCommentHash
};
const updateOperation = { updateOne: { filter: { _id: userModifiedSecret._id, workspace: workspaceId }, update: { $inc: { version: 1 }, $set: sanitizedSecret } } }
updateOperationsToPerform.push(updateOperation)
const updateOperation = {
updateOne: {
filter: { _id: userModifiedSecret._id, workspace: workspaceId },
update: { $inc: { version: 1 }, $set: sanitizedSecret }
}
};
updateOperationsToPerform.push(updateOperation);
} else {
throw UnauthorizedRequestError({ message: "You do not have permission to modify one or more of the requested secrets" })
throw UnauthorizedRequestError({
message: "You do not have permission to modify one or more of the requested secrets"
});
}
})
});
const [bulkModificationInfoError, bulkModificationInfo] = await to(Secret.bulkWrite(updateOperationsToPerform).then())
const [bulkModificationInfoError, bulkModificationInfo] = await to(
Secret.bulkWrite(updateOperationsToPerform).then()
);
if (bulkModificationInfoError) {
if (bulkModificationInfoError instanceof ValidationError) {
throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: bulkModificationInfoError.stack })
throw RouteValidationError({
message: "Unable to apply modifications, please try again",
stack: bulkModificationInfoError.stack
});
}
throw InternalServerError()
throw InternalServerError();
}
if (postHogClient) {
postHogClient.capture({
event: 'secrets modified',
event: "secrets modified",
distinctId: req.user.email,
properties: {
numberOfSecrets: (secretsModificationsRequested ?? []).length,
environment: environmentName,
workspaceId,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
userAgent: req.headers?.["user-agent"]
}
});
}
return res.status(200).send()
}
return res.status(200).send();
};
/**
* Update a secret within workspace with id [workspaceId] and environment [environment]
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const updateSecret = async (req: Request, res: Response) => {
const postHogClient = await TelemetryService.getPostHogClient();
const { workspaceId, environmentName } = req.params
const { workspaceId, environmentName } = req.params;
const secretModificationsRequested: ModifySecretRequestBody = req.body.secret;
const [secretIdUserCanModifyError, secretIdUserCanModify] = await to(Secret.findOne({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then())
const [secretIdUserCanModifyError, secretIdUserCanModify] = await to(
Secret.findOne({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then()
);
if (secretIdUserCanModifyError && !secretIdUserCanModify) {
throw BadRequestError()
throw BadRequestError();
}
const sanitizedSecret: SanitizedSecretModify = {
@ -308,45 +359,53 @@ export const updateSecret = async (req: Request, res: Response) => {
secretCommentCiphertext: secretModificationsRequested.secretCommentCiphertext,
secretCommentIV: secretModificationsRequested.secretCommentIV,
secretCommentTag: secretModificationsRequested.secretCommentTag,
secretCommentHash: secretModificationsRequested.secretCommentHash,
}
secretCommentHash: secretModificationsRequested.secretCommentHash
};
const [error, singleModificationUpdate] = await to(Secret.updateOne({ _id: secretModificationsRequested._id, workspace: workspaceId }, { $inc: { version: 1 }, $set: sanitizedSecret }).then())
const [error, singleModificationUpdate] = await to(
Secret.updateOne(
{ _id: secretModificationsRequested._id, workspace: workspaceId },
{ $inc: { version: 1 }, $set: sanitizedSecret }
).then()
);
if (error instanceof ValidationError) {
throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: error.stack })
throw RouteValidationError({
message: "Unable to apply modifications, please try again",
stack: error.stack
});
}
if (postHogClient) {
postHogClient.capture({
event: 'secrets modified',
event: "secrets modified",
distinctId: req.user.email,
properties: {
numberOfSecrets: 1,
environment: environmentName,
workspaceId,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
userAgent: req.headers?.["user-agent"]
}
});
}
return res.status(200).send(singleModificationUpdate)
}
return res.status(200).send(singleModificationUpdate);
};
/**
* Return secrets for workspace with id [workspaceId], environment [environment] and user
* with id [req.user._id]
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const getSecrets = async (req: Request, res: Response) => {
const postHogClient = await TelemetryService.getPostHogClient();
const { environment } = req.query;
const { workspaceId } = req.params;
let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user
let userEmail: string | undefined = undefined // used for posthog
let userId: Types.ObjectId | undefined = undefined; // used for getting personal secrets for user
let userEmail: string | undefined = undefined; // used for posthog
if (req.user) {
userId = req.user._id;
userEmail = req.user.email;
@ -354,47 +413,50 @@ export const getSecrets = async (req: Request, res: Response) => {
if (req.serviceTokenData) {
userId = req.serviceTokenData.user;
const user = await User.findById(req.serviceTokenData.user, 'email');
const user = await User.findById(req.serviceTokenData.user, "email");
if (!user) throw AccountNotFoundError();
userEmail = user.email;
}
const [err, secrets] = await to(Secret.find(
{
const [err, secrets] = await to(
Secret.find({
workspace: workspaceId,
environment,
$or: [{ user: userId }, { user: { $exists: false } }],
type: { $in: [SECRET_SHARED, SECRET_PERSONAL] }
}
).then())
}).then()
);
if (err) {
throw RouteValidationError({ message: "Failed to get secrets, please try again", stack: err.stack })
throw RouteValidationError({
message: "Failed to get secrets, please try again",
stack: err.stack
});
}
if (postHogClient) {
postHogClient.capture({
event: 'secrets pulled',
event: "secrets pulled",
distinctId: userEmail,
properties: {
numberOfSecrets: (secrets ?? []).length,
environment,
workspaceId,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
userAgent: req.headers?.["user-agent"]
}
});
}
return res.json(secrets)
}
return res.json(secrets);
};
/**
* Return secret with id [secretId]
* @param req
* @param res
* @returns
* @param req
* @param res
* @returns
*/
export const getSecret = async (req: Request, res: Response) => {
// if (postHogClient) {
@ -414,4 +476,4 @@ export const getSecret = async (req: Request, res: Response) => {
return res.status(200).send({
secret: req._secret
});
}
};

@ -3,19 +3,19 @@ import { Request, Response } from "express";
import { ISecret, Secret, ServiceTokenData } from "../../models";
import { IAction, SecretVersion } from "../../ee/models";
import {
SECRET_PERSONAL,
ACTION_ADD_SECRETS,
ACTION_DELETE_SECRETS,
ACTION_READ_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_DELETE_SECRETS,
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
SECRET_PERSONAL,
} from "../../variables";
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
import { EventService } from "../../services";
import { eventPushSecrets } from "../../events";
import { EESecretService, EELogService } from "../../ee/services";
import { TelemetryService, SecretService } from "../../services";
import { EELogService, EESecretService } from "../../ee/services";
import { SecretService, TelemetryService } from "../../services";
import { getChannelFromUserAgent } from "../../utils/posthog";
import { PERMISSION_WRITE_SECRETS } from "../../variables";
import {
@ -25,7 +25,7 @@ import {
} from "../../ee/helpers/checkMembershipPermissions";
import Tag from "../../models/tag";
import _ from "lodash";
import { BatchSecretRequest, BatchSecret } from "../../types/secret";
import { BatchSecret, BatchSecretRequest } from "../../types/secret";
import Folder from "../../models/folder";
import {
getFolderByPath,

@ -1,18 +1,18 @@
import { Request, Response } from 'express';
import { Types } from 'mongoose';
import crypto from 'crypto';
import bcrypt from 'bcrypt';
import { Request, Response } from "express";
import { Types } from "mongoose";
import crypto from "crypto";
import bcrypt from "bcrypt";
import {
ServiceAccount,
ServiceAccountKey,
ServiceAccountOrganizationPermission,
ServiceAccountWorkspacePermission
} from '../../models';
ServiceAccountWorkspacePermission,
} from "../../models";
import {
CreateServiceAccountDto
} from '../../interfaces/serviceAccounts/dto';
import { BadRequestError, ServiceAccountNotFoundError } from '../../utils/errors';
import { getSaltRounds } from '../../config';
CreateServiceAccountDto,
} from "../../interfaces/serviceAccounts/dto";
import { BadRequestError, ServiceAccountNotFoundError } from "../../utils/errors";
import { getSaltRounds } from "../../config";
/**
* Return service account tied to the request (service account) client
@ -23,11 +23,11 @@ export const getCurrentServiceAccount = async (req: Request, res: Response) => {
const serviceAccount = await ServiceAccount.findById(req.serviceAccount._id);
if (!serviceAccount) {
throw ServiceAccountNotFoundError({ message: 'Failed to find service account' });
throw ServiceAccountNotFoundError({ message: "Failed to find service account" });
}
return res.status(200).send({
serviceAccount
serviceAccount,
});
}
@ -42,11 +42,11 @@ export const getServiceAccountById = async (req: Request, res: Response) => {
const serviceAccount = await ServiceAccount.findById(serviceAccountId);
if (!serviceAccount) {
throw ServiceAccountNotFoundError({ message: 'Failed to find service account' });
throw ServiceAccountNotFoundError({ message: "Failed to find service account" });
}
return res.status(200).send({
serviceAccount
serviceAccount,
});
}
@ -71,7 +71,7 @@ export const createServiceAccount = async (req: Request, res: Response) => {
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
}
const secret = crypto.randomBytes(16).toString('base64');
const secret = crypto.randomBytes(16).toString("base64");
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
// create service account
@ -82,7 +82,7 @@ export const createServiceAccount = async (req: Request, res: Response) => {
publicKey,
lastUsed: new Date(),
expiresAt,
secretHash
secretHash,
}).save();
const serviceAccountObj = serviceAccount.toObject();
@ -91,14 +91,14 @@ export const createServiceAccount = async (req: Request, res: Response) => {
// provision default org-level permission for service account
await new ServiceAccountOrganizationPermission({
serviceAccount: serviceAccount._id
serviceAccount: serviceAccount._id,
}).save();
const secretId = Buffer.from(serviceAccount._id.toString(), 'hex').toString('base64');
const secretId = Buffer.from(serviceAccount._id.toString(), "hex").toString("base64");
return res.status(200).send({
serviceAccountAccessKey: `sa.${secretId}.${secret}`,
serviceAccount: serviceAccountObj
serviceAccount: serviceAccountObj,
});
}
@ -114,18 +114,18 @@ export const changeServiceAccountName = async (req: Request, res: Response) => {
const serviceAccount = await ServiceAccount.findOneAndUpdate(
{
_id: new Types.ObjectId(serviceAccountId)
_id: new Types.ObjectId(serviceAccountId),
},
{
name
name,
},
{
new: true
new: true,
}
);
return res.status(200).send({
serviceAccount
serviceAccount,
});
}
@ -140,7 +140,7 @@ export const addServiceAccountKey = async (req: Request, res: Response) => {
const {
workspaceId,
encryptedKey,
nonce
nonce,
} = req.body;
const serviceAccountKey = await new ServiceAccountKey({
@ -148,7 +148,7 @@ export const addServiceAccountKey = async (req: Request, res: Response) => {
nonce,
sender: req.user._id,
serviceAccount: req.serviceAccount._d,
workspace: new Types.ObjectId(workspaceId)
workspace: new Types.ObjectId(workspaceId),
}).save();
return serviceAccountKey;
@ -161,11 +161,11 @@ export const addServiceAccountKey = async (req: Request, res: Response) => {
*/
export const getServiceAccountWorkspacePermissions = async (req: Request, res: Response) => {
const serviceAccountWorkspacePermissions = await ServiceAccountWorkspacePermission.find({
serviceAccount: req.serviceAccount._id
}).populate('workspace');
serviceAccount: req.serviceAccount._id,
}).populate("workspace");
return res.status(200).send({
serviceAccountWorkspacePermissions
serviceAccountWorkspacePermissions,
});
}
@ -182,34 +182,34 @@ export const addServiceAccountWorkspacePermission = async (req: Request, res: Re
read = false,
write = false,
encryptedKey,
nonce
nonce,
} = req.body;
if (!req.membership.workspace.environments.some((e: { name: string; slug: string }) => e.slug === environment)) {
return res.status(400).send({
message: 'Failed to validate workspace environment'
message: "Failed to validate workspace environment",
});
}
const existingPermission = await ServiceAccountWorkspacePermission.findOne({
serviceAccount: new Types.ObjectId(serviceAccountId),
workspace: new Types.ObjectId(workspaceId),
environment
environment,
});
if (existingPermission) throw BadRequestError({ message: 'Failed to add workspace permission to service account due to already-existing ' });
if (existingPermission) throw BadRequestError({ message: "Failed to add workspace permission to service account due to already-existing " });
const serviceAccountWorkspacePermission = await new ServiceAccountWorkspacePermission({
serviceAccount: new Types.ObjectId(serviceAccountId),
workspace: new Types.ObjectId(workspaceId),
environment,
read,
write
write,
}).save();
const existingServiceAccountKey = await ServiceAccountKey.findOne({
serviceAccount: new Types.ObjectId(serviceAccountId),
workspace: new Types.ObjectId(workspaceId)
workspace: new Types.ObjectId(workspaceId),
});
if (!existingServiceAccountKey) {
@ -218,12 +218,12 @@ export const addServiceAccountWorkspacePermission = async (req: Request, res: Re
nonce,
sender: req.user._id,
serviceAccount: new Types.ObjectId(serviceAccountId),
workspace: new Types.ObjectId(workspaceId)
workspace: new Types.ObjectId(workspaceId),
}).save();
}
return res.status(200).send({
serviceAccountWorkspacePermission
serviceAccountWorkspacePermission,
});
}
@ -240,19 +240,19 @@ export const deleteServiceAccountWorkspacePermission = async (req: Request, res:
const { serviceAccount, workspace } = serviceAccountWorkspacePermission;
const count = await ServiceAccountWorkspacePermission.countDocuments({
serviceAccount,
workspace
workspace,
});
if (count === 0) {
await ServiceAccountKey.findOneAndDelete({
serviceAccount,
workspace
workspace,
});
}
}
return res.status(200).send({
serviceAccountWorkspacePermission
serviceAccountWorkspacePermission,
});
}
@ -269,20 +269,20 @@ export const deleteServiceAccount = async (req: Request, res: Response) => {
if (serviceAccount) {
await ServiceAccountKey.deleteMany({
serviceAccount: serviceAccount._id
serviceAccount: serviceAccount._id,
});
await ServiceAccountOrganizationPermission.deleteMany({
serviceAccount: new Types.ObjectId(serviceAccountId)
serviceAccount: new Types.ObjectId(serviceAccountId),
});
await ServiceAccountWorkspacePermission.deleteMany({
serviceAccount: new Types.ObjectId(serviceAccountId)
serviceAccount: new Types.ObjectId(serviceAccountId),
});
}
return res.status(200).send({
serviceAccount
serviceAccount,
});
}
@ -297,10 +297,10 @@ export const getServiceAccountKeys = async (req: Request, res: Response) => {
const serviceAccountKeys = await ServiceAccountKey.find({
serviceAccount: req.serviceAccount._id,
...(workspaceId ? { workspace: new Types.ObjectId(workspaceId) } : {})
...(workspaceId ? { workspace: new Types.ObjectId(workspaceId) } : {}),
});
return res.status(200).send({
serviceAccountKeys
serviceAccountKeys,
});
}

@ -1,13 +1,10 @@
import { Request, Response } from "express";
import crypto from "crypto";
import bcrypt from "bcrypt";
import { User, ServiceAccount, ServiceTokenData } from "../../models";
import { userHasWorkspaceAccess } from "../../ee/helpers/checkMembershipPermissions";
import { ServiceAccount, ServiceTokenData, User } from "../../models";
import {
PERMISSION_READ_SECRETS,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../../variables";
import { getSaltRounds } from "../../config";
import { BadRequestError } from "../../utils/errors";

@ -1,14 +1,14 @@
import { Request, Response } from 'express';
import { User, MembershipOrg } from '../../models';
import { completeAccount } from '../../helpers/user';
import { Request, Response } from "express";
import { MembershipOrg, User } from "../../models";
import { completeAccount } from "../../helpers/user";
import {
initializeDefaultOrg
} from '../../helpers/signup';
import { issueAuthTokens } from '../../helpers/auth';
import { INVITED, ACCEPTED } from '../../variables';
import { standardRequest } from '../../config/request';
import { getLoopsApiKey, getHttpsEnabled } from '../../config';
import { updateSubscriptionOrgQuantity } from '../../helpers/organization';
initializeDefaultOrg,
} from "../../helpers/signup";
import { issueAuthTokens } from "../../helpers/auth";
import { ACCEPTED, INVITED } from "../../variables";
import { standardRequest } from "../../config/request";
import { getHttpsEnabled, getLoopsApiKey } from "../../config";
import { updateSubscriptionOrgQuantity } from "../../helpers/organization";
/**
* Complete setting up user by adding their personal and auth information as part of the
@ -32,7 +32,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
encryptedPrivateKeyTag,
salt,
verifier,
organizationName
organizationName,
}: {
email: string;
firstName: string;
@ -56,7 +56,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
// case 1: user doesn't exist.
// case 2: user has already completed account
return res.status(403).send({
error: 'Failed to complete account for complete user'
error: "Failed to complete account for complete user",
});
}
@ -74,28 +74,28 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
verifier,
});
if (!user)
throw new Error('Failed to complete account for non-existent user'); // ensure user is non-null
throw new Error("Failed to complete account for non-existent user"); // ensure user is non-null
// initialize default organization and workspace
await initializeDefaultOrg({
organizationName,
user
user,
});
// update organization membership statuses that are
// invited to completed with user attached
const membershipsToUpdate = await MembershipOrg.find({
inviteEmail: email,
status: INVITED
status: INVITED,
});
membershipsToUpdate.forEach(async (membership) => {
await updateSubscriptionOrgQuantity({
organizationId: membership.organization.toString()
organizationId: membership.organization.toString(),
});
});
@ -104,11 +104,11 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
await MembershipOrg.updateMany(
{
inviteEmail: email,
status: INVITED
status: INVITED,
},
{
user,
status: ACCEPTED
status: ACCEPTED,
}
);
@ -116,7 +116,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
const tokens = await issueAuthTokens({
userId: user._id,
ip: req.realIP,
userAgent: req.headers['user-agent'] ?? ''
userAgent: req.headers["user-agent"] ?? "",
});
token = tokens.token;
@ -127,27 +127,27 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
"email": email,
"eventName": "Sign Up",
"firstName": firstName,
"lastName": lastName
"lastName": lastName,
}, {
headers: {
"Accept": "application/json",
"Authorization": "Bearer " + (await getLoopsApiKey())
"Authorization": "Bearer " + (await getLoopsApiKey()),
},
});
}
// store (refresh) token in httpOnly cookie
res.cookie('jid', tokens.refreshToken, {
res.cookie("jid", tokens.refreshToken, {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: await getHttpsEnabled()
path: "/",
sameSite: "strict",
secure: await getHttpsEnabled(),
});
return res.status(200).send({
message: 'Successfully set up account',
message: "Successfully set up account",
user,
token
token,
});
};
@ -172,7 +172,7 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
verifier,
} = req.body;
// get user
@ -182,16 +182,16 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
// case 1: user doesn't exist.
// case 2: user has already completed account
return res.status(403).send({
error: 'Failed to complete account for complete user'
error: "Failed to complete account for complete user",
});
}
const membershipOrg = await MembershipOrg.findOne({
inviteEmail: email,
status: INVITED
status: INVITED,
});
if (!membershipOrg) throw new Error('Failed to find invitations for email');
if (!membershipOrg) throw new Error("Failed to find invitations for email");
// complete setting up user's account
user = await completeAccount({
@ -207,33 +207,33 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
verifier,
});
if (!user)
throw new Error('Failed to complete account for non-existent user');
throw new Error("Failed to complete account for non-existent user");
// update organization membership statuses that are
// invited to completed with user attached
const membershipsToUpdate = await MembershipOrg.find({
inviteEmail: email,
status: INVITED
status: INVITED,
});
membershipsToUpdate.forEach(async (membership) => {
await updateSubscriptionOrgQuantity({
organizationId: membership.organization.toString()
organizationId: membership.organization.toString(),
});
});
await MembershipOrg.updateMany(
{
inviteEmail: email,
status: INVITED
status: INVITED,
},
{
user,
status: ACCEPTED
status: ACCEPTED,
}
);
@ -241,22 +241,22 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
const tokens = await issueAuthTokens({
userId: user._id,
ip: req.realIP,
userAgent: req.headers['user-agent'] ?? ''
userAgent: req.headers["user-agent"] ?? "",
});
token = tokens.token;
// store (refresh) token in httpOnly cookie
res.cookie('jid', tokens.refreshToken, {
res.cookie("jid", tokens.refreshToken, {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: await getHttpsEnabled()
path: "/",
sameSite: "strict",
secure: await getHttpsEnabled(),
});
return res.status(200).send({
message: 'Successfully set up account',
message: "Successfully set up account",
user,
token
token,
});
};

@ -1,71 +1,65 @@
import { Request, Response } from 'express';
import { Types } from 'mongoose';
import {
Membership, Secret,
} from '../../models';
import Tag, { ITag } from '../../models/tag';
import { Builder } from "builder-pattern"
import to from 'await-to-js';
import { BadRequestError, UnauthorizedRequestError } from '../../utils/errors';
import { MongoError } from 'mongodb';
import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissions';
import { Request, Response } from "express";
import { Types } from "mongoose";
import { Membership, Secret } from "../../models";
import Tag, { ITag } from "../../models/tag";
import { Builder } from "builder-pattern";
import to from "await-to-js";
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
import { MongoError } from "mongodb";
export const createWorkspaceTag = async (req: Request, res: Response) => {
const { workspaceId } = req.params
const { name, slug } = req.body
const sanitizedTagToCreate = Builder<ITag>()
.name(name)
.workspace(new Types.ObjectId(workspaceId))
.slug(slug)
.user(new Types.ObjectId(req.user._id))
.build();
const { workspaceId } = req.params;
const { name, slug } = req.body;
const sanitizedTagToCreate = Builder<ITag>()
.name(name)
.workspace(new Types.ObjectId(workspaceId))
.slug(slug)
.user(new Types.ObjectId(req.user._id))
.build();
const [err, createdTag] = await to(Tag.create(sanitizedTagToCreate))
const [err, createdTag] = await to(Tag.create(sanitizedTagToCreate));
if (err) {
if ((err as MongoError).code === 11000) {
throw BadRequestError({ message: "Tags must be unique in a workspace" })
}
if (err) {
if ((err as MongoError).code === 11000) {
throw BadRequestError({ message: "Tags must be unique in a workspace" });
}
throw err
}
throw err;
}
res.json(createdTag)
}
res.json(createdTag);
};
export const deleteWorkspaceTag = async (req: Request, res: Response) => {
const { tagId } = req.params
const { tagId } = req.params;
const tagFromDB = await Tag.findById(tagId)
if (!tagFromDB) {
throw BadRequestError()
}
const tagFromDB = await Tag.findById(tagId);
if (!tagFromDB) {
throw BadRequestError();
}
// can only delete if the request user is one that belongs to the same workspace as the tag
const membership = await Membership.findOne({
user: req.user,
workspace: tagFromDB.workspace
});
// can only delete if the request user is one that belongs to the same workspace as the tag
const membership = await Membership.findOne({
user: req.user,
workspace: tagFromDB.workspace
});
if (!membership) {
UnauthorizedRequestError({ message: 'Failed to validate membership' });
}
if (!membership) {
UnauthorizedRequestError({ message: "Failed to validate membership" });
}
const result = await Tag.findByIdAndDelete(tagId);
const result = await Tag.findByIdAndDelete(tagId);
// remove the tag from secrets
await Secret.updateMany(
{ tags: { $in: [tagId] } },
{ $pull: { tags: tagId } }
);
// remove the tag from secrets
await Secret.updateMany({ tags: { $in: [tagId] } }, { $pull: { tags: tagId } });
res.json(result);
}
res.json(result);
};
export const getWorkspaceTags = async (req: Request, res: Response) => {
const { workspaceId } = req.params
const workspaceTags = await Tag.find({ workspace: workspaceId })
return res.json({
workspaceTags
})
}
const { workspaceId } = req.params;
const workspaceTags = await Tag.find({ workspace: workspaceId });
return res.json({
workspaceTags
});
};

@ -1,8 +1,8 @@
import { Request, Response } from 'express';
import { Request, Response } from "express";
import {
MembershipOrg,
User,
MembershipOrg
} from '../../models';
} from "../../models";
/**
* Return the current user.
@ -38,10 +38,10 @@ export const getMe = async (req: Request, res: Response) => {
*/
const user = await User
.findById(req.user._id)
.select('+salt +publicKey +encryptedPrivateKey +iv +tag +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag');
.select("+salt +publicKey +encryptedPrivateKey +iv +tag +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag");
return res.status(200).send({
user
user,
});
}
@ -60,7 +60,7 @@ export const updateMyMfaEnabled = async (req: Request, res: Response) => {
if (isMfaEnabled) {
// TODO: adapt this route/controller
// to work for different forms of MFA
req.user.mfaMethods = ['email'];
req.user.mfaMethods = ["email"];
} else {
req.user.mfaMethods = [];
}
@ -70,7 +70,7 @@ export const updateMyMfaEnabled = async (req: Request, res: Response) => {
const user = req.user;
return res.status(200).send({
user
user,
});
}
@ -109,11 +109,11 @@ export const getMyOrganizations = async (req: Request, res: Response) => {
*/
const organizations = (
await MembershipOrg.find({
user: req.user._id
}).populate('organization')
user: req.user._id,
}).populate("organization")
).map((m) => m.organization);
return res.status(200).send({
organizations
organizations,
});
}

@ -1,25 +1,19 @@
import { Request, Response } from 'express';
import { Types } from 'mongoose';
import { Request, Response } from "express";
import { Types } from "mongoose";
import {
Workspace,
Secret,
Membership,
MembershipOrg,
Integration,
IntegrationAuth,
Key,
IUser,
ServiceToken,
ServiceTokenData
} from '../../models';
Membership,
ServiceTokenData,
Workspace,
} from "../../models";
import {
v2PushSecrets as push,
pullSecrets as pull,
reformatPullSecrets
} from '../../helpers/secret';
import { pushKeys } from '../../helpers/key';
import { TelemetryService, EventService } from '../../services';
import { eventPushSecrets } from '../../events';
v2PushSecrets as push,
reformatPullSecrets,
} from "../../helpers/secret";
import { pushKeys } from "../../helpers/key";
import { EventService, TelemetryService } from "../../services";
import { eventPushSecrets } from "../../events";
interface V2PushSecret {
type: string; // personal or shared
@ -54,12 +48,12 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
// validate environment
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error('Failed to validate environment');
throw new Error("Failed to validate environment");
}
// sanitize secrets
secrets = secrets.filter(
(s: V2PushSecret) => s.secretKeyCiphertext !== '' && s.secretValueCiphertext !== ''
(s: V2PushSecret) => s.secretKeyCiphertext !== "" && s.secretValueCiphertext !== ""
);
await push({
@ -67,26 +61,26 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
workspaceId,
environment,
secrets,
channel: channel ? channel : 'cli',
ipAddress: req.realIP
channel: channel ? channel : "cli",
ipAddress: req.realIP,
});
await pushKeys({
userId: req.user._id,
workspaceId,
keys
keys,
});
if (postHogClient) {
postHogClient.capture({
event: 'secrets pushed',
event: "secrets pushed",
distinctId: req.user.email,
properties: {
numberOfSecrets: secrets.length,
environment,
workspaceId,
channel: channel ? channel : 'cli'
}
channel: channel ? channel : "cli",
},
});
}
@ -94,12 +88,12 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
EventService.handleEvent({
event: eventPushSecrets({
workspaceId: new Types.ObjectId(workspaceId),
environment
})
environment,
}),
});
return res.status(200).send({
message: 'Successfully uploaded workspace secrets'
message: "Successfully uploaded workspace secrets",
});
};
@ -126,18 +120,18 @@ export const pullSecrets = async (req: Request, res: Response) => {
// validate environment
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error('Failed to validate environment');
throw new Error("Failed to validate environment");
}
secrets = await pull({
userId,
workspaceId,
environment,
channel: channel ? channel : 'cli',
ipAddress: req.realIP
channel: channel ? channel : "cli",
ipAddress: req.realIP,
});
if (channel !== 'cli') {
if (channel !== "cli") {
secrets = reformatPullSecrets({ secrets });
}
@ -145,18 +139,18 @@ export const pullSecrets = async (req: Request, res: Response) => {
// capture secrets pushed event in production
postHogClient.capture({
distinctId: req.user.email,
event: 'secrets pulled',
event: "secrets pulled",
properties: {
numberOfSecrets: secrets.length,
environment,
workspaceId,
channel: channel ? channel : 'cli'
}
channel: channel ? channel : "cli",
},
});
}
return res.status(200).send({
secrets
secrets,
});
};
@ -194,10 +188,10 @@ export const getWorkspaceKey = async (req: Request, res: Response) => {
key = await Key.findOne({
workspace: workspaceId,
receiver: req.user._id
}).populate('sender', '+publicKey');
receiver: req.user._id,
}).populate("sender", "+publicKey");
if (!key) throw new Error('Failed to find workspace key');
if (!key) throw new Error("Failed to find workspace key");
return res.status(200).json(key);
}
@ -209,12 +203,12 @@ export const getWorkspaceServiceTokenData = async (
const serviceTokenData = await ServiceTokenData
.find({
workspace: workspaceId
workspace: workspaceId,
})
.select('+encryptedKey +iv +tag');
.select("+encryptedKey +iv +tag");
return res.status(200).send({
serviceTokenData
serviceTokenData,
});
}
@ -261,11 +255,11 @@ export const getWorkspaceMemberships = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
const memberships = await Membership.find({
workspace: workspaceId
}).populate('user', '+publicKey');
workspace: workspaceId,
}).populate("user", "+publicKey");
return res.status(200).send({
memberships
memberships,
});
}
@ -330,21 +324,21 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) =>
}
*/
const {
membershipId
membershipId,
} = req.params;
const { role } = req.body;
const membership = await Membership.findByIdAndUpdate(
membershipId,
{
role
role,
}, {
new: true
new: true,
}
);
return res.status(200).send({
membership
membership,
});
}
@ -392,20 +386,20 @@ export const deleteWorkspaceMembership = async (req: Request, res: Response) =>
}
*/
const {
membershipId
membershipId,
} = req.params;
const membership = await Membership.findByIdAndDelete(membershipId);
if (!membership) throw new Error('Failed to delete workspace membership');
if (!membership) throw new Error("Failed to delete workspace membership");
await Key.deleteMany({
receiver: membership.user,
workspace: membership.workspace
workspace: membership.workspace,
});
return res.status(200).send({
membership
membership,
});
}
@ -421,18 +415,18 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
const workspace = await Workspace.findOneAndUpdate(
{
_id: workspaceId
_id: workspaceId,
},
{
autoCapitalization
autoCapitalization,
},
{
new: true
new: true,
}
);
return res.status(200).send({
message: 'Successfully changed autoCapitalization setting',
workspace
message: "Successfully changed autoCapitalization setting",
workspace,
});
};

@ -1,29 +1,29 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import { Request, Response } from 'express';
import jwt from 'jsonwebtoken';
import * as Sentry from '@sentry/node';
import * as bigintConversion from 'bigint-conversion';
const jsrp = require('jsrp');
import { User, LoginSRPDetail } from '../../models';
import { issueAuthTokens, createToken, validateProviderAuthToken } from '../../helpers/auth';
import { checkUserDevice } from '../../helpers/user';
import { sendMail } from '../../helpers/nodemailer';
import { TokenService } from '../../services';
import { EELogService } from '../../ee/services';
import { BadRequestError, InternalServerError } from '../../utils/errors';
import { Request, Response } from "express";
import jwt from "jsonwebtoken";
import * as Sentry from "@sentry/node";
import * as bigintConversion from "bigint-conversion";
const jsrp = require("jsrp");
import { LoginSRPDetail, User } from "../../models";
import { createToken, issueAuthTokens, validateProviderAuthToken } from "../../helpers/auth";
import { checkUserDevice } from "../../helpers/user";
import { sendMail } from "../../helpers/nodemailer";
import { TokenService } from "../../services";
import { EELogService } from "../../ee/services";
import { BadRequestError, InternalServerError } from "../../utils/errors";
import {
ACTION_LOGIN,
TOKEN_EMAIL_MFA,
ACTION_LOGIN
} from '../../variables';
import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this
} from "../../variables";
import { getChannelFromUserAgent } from "../../utils/posthog"; // TODO: move this
import {
getHttpsEnabled,
getJwtMfaLifetime,
getJwtMfaSecret,
getHttpsEnabled,
} from '../../config';
import { AuthProvider } from '../../models/user';
} from "../../config";
import { AuthProvider } from "../../models/user";
declare module 'jsonwebtoken' {
declare module "jsonwebtoken" {
export interface ProviderAuthJwtPayload extends jwt.JwtPayload {
userId: string;
email: string;
@ -43,7 +43,7 @@ export const login1 = async (req: Request, res: Response) => {
const {
email,
providerAuthToken,
clientPublicKey
clientPublicKey,
}: {
email: string;
clientPublicKey: string,
@ -52,9 +52,9 @@ export const login1 = async (req: Request, res: Response) => {
const user = await User.findOne({
email,
}).select('+salt +verifier');
}).select("+salt +verifier");
if (!user) throw new Error('Failed to find user');
if (!user) throw new Error("Failed to find user");
if (user.authProvider) {
await validateProviderAuthToken({
@ -68,13 +68,13 @@ export const login1 = async (req: Request, res: Response) => {
server.init(
{
salt: user.salt,
verifier: user.verifier
verifier: user.verifier,
},
async () => {
// generate server-side public key
const serverPublicKey = server.getPublicKey();
await LoginSRPDetail.findOneAndReplace({
email: email
email: email,
}, {
email,
userId: user.id,
@ -84,7 +84,7 @@ export const login1 = async (req: Request, res: Response) => {
return res.status(200).send({
serverPublicKey,
salt: user.salt
salt: user.salt,
});
}
);
@ -92,7 +92,7 @@ export const login1 = async (req: Request, res: Response) => {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to start authentication process'
message: "Failed to start authentication process",
});
}
};
@ -107,15 +107,15 @@ export const login1 = async (req: Request, res: Response) => {
export const login2 = async (req: Request, res: Response) => {
try {
if (!req.headers['user-agent']) throw InternalServerError({ message: 'User-Agent header is required' });
if (!req.headers["user-agent"]) throw InternalServerError({ message: "User-Agent header is required" });
const { email, clientProof, providerAuthToken } = req.body;
const user = await User.findOne({
email,
}).select('+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices');
}).select("+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices");
if (!user) throw new Error('Failed to find user');
if (!user) throw new Error("Failed to find user");
if (user.authProvider) {
await validateProviderAuthToken({
@ -136,7 +136,7 @@ export const login2 = async (req: Request, res: Response) => {
{
salt: user.salt,
verifier: user.verifier,
b: loginSRPDetail.serverBInt
b: loginSRPDetail.serverBInt,
},
async () => {
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
@ -150,52 +150,52 @@ export const login2 = async (req: Request, res: Response) => {
// generate temporary MFA token
const token = createToken({
payload: {
userId: user._id.toString()
userId: user._id.toString(),
},
expiresIn: await getJwtMfaLifetime(),
secret: await getJwtMfaSecret()
secret: await getJwtMfaSecret(),
});
const code = await TokenService.createToken({
type: TOKEN_EMAIL_MFA,
email
email,
});
// send MFA code [code] to [email]
await sendMail({
template: 'emailMfa.handlebars',
subjectLine: 'Infisical MFA code',
template: "emailMfa.handlebars",
subjectLine: "Infisical MFA code",
recipients: [user.email],
substitutions: {
code
}
code,
},
});
return res.status(200).send({
mfaEnabled: true,
token
token,
});
}
await checkUserDevice({
user,
ip: req.realIP,
userAgent: req.headers['user-agent'] ?? ''
userAgent: req.headers["user-agent"] ?? "",
});
// issue tokens
const tokens = await issueAuthTokens({
userId: user._id,
ip: req.realIP,
userAgent: req.headers['user-agent'] ?? ''
userAgent: req.headers["user-agent"] ?? "",
});
// store (refresh) token in httpOnly cookie
res.cookie('jid', tokens.refreshToken, {
res.cookie("jid", tokens.refreshToken, {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: await getHttpsEnabled()
path: "/",
sameSite: "strict",
secure: await getHttpsEnabled(),
});
// case: user does not have MFA enablgged
@ -221,7 +221,7 @@ export const login2 = async (req: Request, res: Response) => {
publicKey: user.publicKey,
encryptedPrivateKey: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag
tag: user.tag,
}
if (
@ -236,21 +236,21 @@ export const login2 = async (req: Request, res: Response) => {
const loginAction = await EELogService.createAction({
name: ACTION_LOGIN,
userId: user._id
userId: user._id,
});
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers['user-agent']),
ipAddress: req.realIP
channel: getChannelFromUserAgent(req.headers["user-agent"]),
ipAddress: req.realIP,
});
return res.status(200).send(response);
}
return res.status(400).send({
message: 'Failed to authenticate. Try again?'
message: "Failed to authenticate. Try again?",
});
}
);
@ -258,7 +258,7 @@ export const login2 = async (req: Request, res: Response) => {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to authenticate. Try again?'
message: "Failed to authenticate. Try again?",
});
}
};

@ -1,7 +1,7 @@
import * as secretsController from './secretsController';
import * as workspacesController from './workspacesController';
import * as authController from './authController';
import * as signupController from './signupController';
import * as secretsController from "./secretsController";
import * as workspacesController from "./workspacesController";
import * as authController from "./authController";
import * as signupController from "./signupController";
export {
authController,

@ -1,6 +1,6 @@
import { Request, Response } from "express";
import { Types } from "mongoose";
import { SecretService, EventService } from "../../services";
import { EventService, SecretService } from "../../services";
import { eventPushSecrets } from "../../events";
import { BotService } from "../../services";
import { repackageSecretToRaw } from "../../helpers/secrets";
@ -25,18 +25,18 @@ export const getSecretsRaw = async (req: Request, res: Response) => {
});
const key = await BotService.getWorkspaceKeyWithBot({
workspaceId: new Types.ObjectId(workspaceId)
workspaceId: new Types.ObjectId(workspaceId),
});
return res.status(200).send({
secrets: secrets.map((secret) => {
const rep = repackageSecretToRaw({
secret,
key
key,
});
return rep;
})
}),
});
};
@ -62,14 +62,14 @@ export const getSecretByNameRaw = async (req: Request, res: Response) => {
});
const key = await BotService.getWorkspaceKeyWithBot({
workspaceId: new Types.ObjectId(workspaceId)
workspaceId: new Types.ObjectId(workspaceId),
});
return res.status(200).send({
secret: repackageSecretToRaw({
secret,
key
})
key,
}),
});
};
@ -86,26 +86,26 @@ export const createSecretRaw = async (req: Request, res: Response) => {
type,
secretValue,
secretComment,
secretPath = "/"
secretPath = "/",
} = req.body;
const key = await BotService.getWorkspaceKeyWithBot({
workspaceId: new Types.ObjectId(workspaceId)
workspaceId: new Types.ObjectId(workspaceId),
});
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8({
plaintext: secretName,
key
key,
});
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8({
plaintext: secretValue,
key
key,
});
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8({
plaintext: secretComment,
key
key,
});
const secret = await SecretService.createSecret({
@ -123,7 +123,7 @@ export const createSecretRaw = async (req: Request, res: Response) => {
secretPath,
secretCommentCiphertext: secretCommentEncrypted.ciphertext,
secretCommentIV: secretCommentEncrypted.iv,
secretCommentTag: secretCommentEncrypted.tag
secretCommentTag: secretCommentEncrypted.tag,
});
await EventService.handleEvent({
@ -139,8 +139,8 @@ export const createSecretRaw = async (req: Request, res: Response) => {
return res.status(200).send({
secret: repackageSecretToRaw({
secret: secretWithoutBlindIndex,
key
})
key,
}),
});
}
@ -160,12 +160,12 @@ export const updateSecretByNameRaw = async (req: Request, res: Response) => {
} = req.body;
const key = await BotService.getWorkspaceKeyWithBot({
workspaceId: new Types.ObjectId(workspaceId)
workspaceId: new Types.ObjectId(workspaceId),
});
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8({
plaintext: secretValue,
key
key,
});
const secret = await SecretService.updateSecret({
@ -190,8 +190,8 @@ export const updateSecretByNameRaw = async (req: Request, res: Response) => {
return res.status(200).send({
secret: repackageSecretToRaw({
secret,
key
})
key,
}),
});
};
@ -206,7 +206,7 @@ export const deleteSecretByNameRaw = async (req: Request, res: Response) => {
workspaceId,
environment,
type,
secretPath = "/"
secretPath = "/",
} = req.body;
const { secret } = await SecretService.deleteSecret({
@ -226,14 +226,14 @@ export const deleteSecretByNameRaw = async (req: Request, res: Response) => {
});
const key = await BotService.getWorkspaceKeyWithBot({
workspaceId: new Types.ObjectId(workspaceId)
workspaceId: new Types.ObjectId(workspaceId),
});
return res.status(200).send({
secret: repackageSecretToRaw({
secret,
key
})
key,
}),
});
};
@ -324,7 +324,7 @@ export const createSecret = async (req: Request, res: Response) => {
secretPath,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
secretCommentTag,
});
await EventService.handleEvent({
@ -395,7 +395,7 @@ export const deleteSecretByName = async (req: Request, res: Response) => {
workspaceId,
environment,
type,
secretPath = "/"
secretPath = "/",
} = req.body;
const { secret } = await SecretService.deleteSecret({

@ -1,17 +1,17 @@
import jwt from 'jsonwebtoken';
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { User, MembershipOrg } from '../../models';
import { completeAccount } from '../../helpers/user';
import jwt from "jsonwebtoken";
import { Request, Response } from "express";
import * as Sentry from "@sentry/node";
import { MembershipOrg, User } from "../../models";
import { completeAccount } from "../../helpers/user";
import {
initializeDefaultOrg
} from '../../helpers/signup';
import { issueAuthTokens, validateProviderAuthToken } from '../../helpers/auth';
import { INVITED, ACCEPTED } from '../../variables';
import { standardRequest } from '../../config/request';
import { getLoopsApiKey, getHttpsEnabled, getJwtSignupSecret } from '../../config';
import { BadRequestError } from '../../utils/errors';
import { TelemetryService } from '../../services';
initializeDefaultOrg,
} from "../../helpers/signup";
import { issueAuthTokens, validateProviderAuthToken } from "../../helpers/auth";
import { ACCEPTED, INVITED } from "../../variables";
import { standardRequest } from "../../config/request";
import { getHttpsEnabled, getJwtSignupSecret, getLoopsApiKey } from "../../config";
import { BadRequestError } from "../../utils/errors";
import { TelemetryService } from "../../services";
/**
* Complete setting up user by adding their personal and auth information as part of the
@ -63,7 +63,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
// case 1: user doesn't exist.
// case 2: user has already completed account
return res.status(403).send({
error: 'Failed to complete account for complete user'
error: "Failed to complete account for complete user",
});
}
@ -74,16 +74,16 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
user,
});
} else {
const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>req.headers['authorization']?.split(' ', 2) ?? [null, null]
const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>req.headers["authorization"]?.split(" ", 2) ?? [null, null]
if (AUTH_TOKEN_TYPE === null) {
throw BadRequestError({ message: `Missing Authorization Header in the request header.` });
throw BadRequestError({ message: "Missing Authorization Header in the request header." });
}
if (AUTH_TOKEN_TYPE.toLowerCase() !== 'bearer') {
if (AUTH_TOKEN_TYPE.toLowerCase() !== "bearer") {
throw BadRequestError({ message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.` })
}
if (AUTH_TOKEN_VALUE === null) {
throw BadRequestError({
message: 'Missing Authorization Body in the request header',
message: "Missing Authorization Body in the request header",
})
}
@ -110,16 +110,16 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
verifier,
});
if (!user)
throw new Error('Failed to complete account for non-existent user'); // ensure user is non-null
throw new Error("Failed to complete account for non-existent user"); // ensure user is non-null
// initialize default organization and workspace
await initializeDefaultOrg({
organizationName,
user
user,
});
// update organization membership statuses that are
@ -127,11 +127,11 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
await MembershipOrg.updateMany(
{
inviteEmail: email,
status: INVITED
status: INVITED,
},
{
user,
status: ACCEPTED
status: ACCEPTED,
}
);
@ -139,7 +139,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
const tokens = await issueAuthTokens({
userId: user._id,
ip: req.realIP,
userAgent: req.headers['user-agent'] ?? ''
userAgent: req.headers["user-agent"] ?? "",
});
token = tokens.token;
@ -150,45 +150,45 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
"email": email,
"eventName": "Sign Up",
"firstName": firstName,
"lastName": lastName
"lastName": lastName,
}, {
headers: {
"Accept": "application/json",
"Authorization": "Bearer " + (await getLoopsApiKey())
"Authorization": "Bearer " + (await getLoopsApiKey()),
},
});
}
// store (refresh) token in httpOnly cookie
res.cookie('jid', tokens.refreshToken, {
res.cookie("jid", tokens.refreshToken, {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: await getHttpsEnabled()
path: "/",
sameSite: "strict",
secure: await getHttpsEnabled(),
});
const postHogClient = await TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({
event: 'User Signed Up',
event: "User Signed Up",
distinctId: email,
properties: {
email,
attributionSource
}
attributionSource,
},
});
}
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to complete account setup'
message: "Failed to complete account setup",
});
}
return res.status(200).send({
message: 'Successfully set up account',
message: "Successfully set up account",
user,
token
token,
});
};

@ -1,7 +1,7 @@
import { Request, Response } from 'express';
import { Types } from 'mongoose';
import { Secret } from '../../models';
import { SecretService } from'../../services';
import { Request, Response } from "express";
import { Types } from "mongoose";
import { Secret } from "../../models";
import { SecretService } from"../../services";
/**
* Return whether or not all secrets in workspace with id [workspaceId]
@ -16,8 +16,8 @@ export const getWorkspaceBlindIndexStatus = async (req: Request, res: Response)
const secretsWithoutBlindIndex = await Secret.countDocuments({
workspace: new Types.ObjectId(workspaceId),
secretBlindIndex: {
$exists: false
}
$exists: false,
},
});
return res.status(200).send(secretsWithoutBlindIndex === 0);
@ -30,11 +30,11 @@ export const getWorkspaceSecrets = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
const secrets = await Secret.find({
workspace: new Types.ObjectId (workspaceId)
workspace: new Types.ObjectId (workspaceId),
});
return res.status(200).send({
secrets
secrets,
});
}
@ -51,14 +51,14 @@ export const nameWorkspaceSecrets = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
const {
secretsToUpdate
secretsToUpdate,
}: {
secretsToUpdate: SecretToUpdate[];
} = req.body;
// get secret blind index salt
const salt = await SecretService.getSecretBlindIndexSalt({
workspaceId: new Types.ObjectId(workspaceId)
workspaceId: new Types.ObjectId(workspaceId),
});
// update secret blind indices
@ -66,18 +66,18 @@ export const nameWorkspaceSecrets = async (req: Request, res: Response) => {
secretsToUpdate.map(async (secretToUpdate: SecretToUpdate) => {
const secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({
secretName: secretToUpdate.secretName,
salt
salt,
});
return ({
updateOne: {
filter: {
_id: new Types.ObjectId(secretToUpdate._id)
_id: new Types.ObjectId(secretToUpdate._id),
},
update: {
secretBlindIndex
}
}
secretBlindIndex,
},
},
});
})
);
@ -85,6 +85,6 @@ export const nameWorkspaceSecrets = async (req: Request, res: Response) => {
await Secret.bulkWrite(operations);
return res.status(200).send({
message: 'Successfully named workspace secrets'
message: "Successfully named workspace secrets",
});
}

@ -1,6 +1,6 @@
import { Request, Response } from 'express';
import { Action, SecretVersion } from '../../models';
import { ActionNotFoundError } from '../../../utils/errors';
import { Request, Response } from "express";
import { Action } from "../../models";
import { ActionNotFoundError } from "../../../utils/errors";
export const getAction = async (req: Request, res: Response) => {
let action;
@ -10,21 +10,21 @@ export const getAction = async (req: Request, res: Response) => {
action = await Action
.findById(actionId)
.populate([
'payload.secretVersions.oldSecretVersion',
'payload.secretVersions.newSecretVersion'
"payload.secretVersions.oldSecretVersion",
"payload.secretVersions.newSecretVersion",
]);
if (!action) throw ActionNotFoundError({
message: 'Failed to find action'
message: "Failed to find action",
});
} catch (err) {
throw ActionNotFoundError({
message: 'Failed to find action'
message: "Failed to find action",
});
}
return res.status(200).send({
action
action,
});
}

@ -1,7 +1,7 @@
import { Request, Response } from 'express';
import { EELicenseService } from '../../services';
import { getLicenseServerUrl } from '../../../config';
import { licenseServerKeyRequest } from '../../../config/request';
import { Request, Response } from "express";
import { EELicenseService } from "../../services";
import { getLicenseServerUrl } from "../../../config";
import { licenseServerKeyRequest } from "../../../config/request";
/**
* Return available cloud product information.
@ -11,9 +11,9 @@ import { licenseServerKeyRequest } from '../../../config/request';
* @returns
*/
export const getCloudProducts = async (req: Request, res: Response) => {
const billingCycle = req.query['billing-cycle'] as string;
const billingCycle = req.query["billing-cycle"] as string;
if (EELicenseService.instanceType === 'cloud') {
if (EELicenseService.instanceType === "cloud") {
const { data } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
);
@ -23,6 +23,6 @@ export const getCloudProducts = async (req: Request, res: Response) => {
return res.status(200).send({
head: [],
rows: []
rows: [],
});
}

@ -1,11 +1,11 @@
import * as stripeController from './stripeController';
import * as secretController from './secretController';
import * as secretSnapshotController from './secretSnapshotController';
import * as organizationsController from './organizationsController';
import * as workspaceController from './workspaceController';
import * as actionController from './actionController';
import * as membershipController from './membershipController';
import * as cloudProductsController from './cloudProductsController';
import * as stripeController from "./stripeController";
import * as secretController from "./secretController";
import * as secretSnapshotController from "./secretSnapshotController";
import * as organizationsController from "./organizationsController";
import * as workspaceController from "./workspaceController";
import * as actionController from "./actionController";
import * as membershipController from "./membershipController";
import * as cloudProductsController from "./cloudProductsController";
export {
stripeController,
@ -15,5 +15,5 @@ export {
workspaceController,
actionController,
membershipController,
cloudProductsController
cloudProductsController,
}

@ -3,7 +3,7 @@ import { Membership, Workspace } from "../../../models";
import { IMembershipPermission } from "../../../models/membership";
import { BadRequestError, UnauthorizedRequestError } from "../../../utils/errors";
import { ADMIN, MEMBER } from "../../../variables/organization";
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from '../../../variables';
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../../variables";
import { Builder } from "builder-pattern"
import _ from "lodash";
@ -39,7 +39,7 @@ export const denyMembershipPermissions = async (req: Request, res: Response) =>
throw BadRequestError({ message: "Something went wrong when locating the related workspace" })
}
const uniqueEnvironmentSlugs = new Set(_.uniq(_.map(relatedWorkspace.environments, 'slug')));
const uniqueEnvironmentSlugs = new Set(_.uniq(_.map(relatedWorkspace.environments, "slug")));
sanitizedMembershipPermissionsUnique.forEach(permission => {
if (!uniqueEnvironmentSlugs.has(permission.environmentSlug)) {
@ -59,6 +59,6 @@ export const denyMembershipPermissions = async (req: Request, res: Response) =>
}
res.send({
permissionsDenied: updatedMembershipWithPermissions.deniedPermissions
permissionsDenied: updatedMembershipWithPermissions.deniedPermissions,
})
}

@ -1,7 +1,7 @@
import { Request, Response } from 'express';
import { getLicenseServerUrl } from '../../../config';
import { licenseServerKeyRequest } from '../../../config/request';
import { EELicenseService } from '../../services';
import { Request, Response } from "express";
import { getLicenseServerUrl } from "../../../config";
import { licenseServerKeyRequest } from "../../../config/request";
import { EELicenseService } from "../../services";
/**
* Return the organization's current plan and allowed feature set
@ -25,13 +25,13 @@ export const getOrganizationPlan = async (req: Request, res: Response) => {
*/
export const updateOrganizationPlan = async (req: Request, res: Response) => {
const {
productId
productId,
} = req.body;
const { data } = await licenseServerKeyRequest.patch(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/cloud-plan`,
{
productId
productId,
}
);
@ -47,7 +47,7 @@ export const getOrganizationPmtMethods = async (req: Request, res: Response) =>
);
return res.status(200).send({
pmtMethods
pmtMethods,
});
}
@ -57,19 +57,19 @@ export const getOrganizationPmtMethods = async (req: Request, res: Response) =>
export const addOrganizationPmtMethod = async (req: Request, res: Response) => {
const {
success_url,
cancel_url
cancel_url,
} = req.body;
const { data: { url } } = await licenseServerKeyRequest.post(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods`,
{
success_url,
cancel_url
cancel_url,
}
);
return res.status(200).send({
url
url,
});
}

@ -17,11 +17,11 @@ export const getSecretSnapshot = async (req: Request, res: Response) => {
const secretSnapshot = await SecretSnapshot.findById(secretSnapshotId)
.lean()
.populate<{ secretVersions: ISecretVersion[] }>({
path: 'secretVersions',
path: "secretVersions",
populate: {
path: 'tags',
model: 'Tag'
}
path: "tags",
model: "Tag",
},
})
.populate<{ folderVersion: TFolderRootVersionSchema }>("folderVersion");

@ -1,6 +1,6 @@
import { Request, Response } from 'express';
import Stripe from 'stripe';
import { getStripeSecretKey, getStripeWebhookSecret } from '../../../config';
import { Request, Response } from "express";
import Stripe from "stripe";
import { getStripeSecretKey, getStripeWebhookSecret } from "../../../config";
/**
* Handle service provisioning/un-provisioning via Stripe
@ -10,11 +10,11 @@ import { getStripeSecretKey, getStripeWebhookSecret } from '../../../config';
*/
export const handleWebhook = async (req: Request, res: Response) => {
const stripe = new Stripe(await getStripeSecretKey(), {
apiVersion: '2022-08-01'
apiVersion: "2022-08-01",
});
// check request for valid stripe signature
const sig = req.headers['stripe-signature'] as string;
const sig = req.headers["stripe-signature"] as string;
const event = stripe.webhooks.constructEvent(
req.body,
sig,
@ -22,7 +22,7 @@ export const handleWebhook = async (req: Request, res: Response) => {
);
switch (event.type) {
case '':
case "":
break;
default:
}

@ -2,11 +2,11 @@ import { Request, Response } from "express";
import { PipelineStage, Types } from "mongoose";
import { Secret } from "../../../models";
import {
SecretSnapshot,
Log,
SecretVersion,
ISecretVersion,
FolderVersion,
ISecretVersion,
Log,
SecretSnapshot,
SecretVersion,
TFolderRootVersionSchema,
} from "../../models";
import { EESecretService } from "../../services";

@ -1,17 +1,17 @@
import { Types } from 'mongoose';
import { Action } from '../models';
import { Types } from "mongoose";
import { Action } from "../models";
import {
getLatestNSecretSecretVersionIds,
getLatestSecretVersionIds,
getLatestNSecretSecretVersionIds
} from '../helpers/secretVersion';
} from "../helpers/secretVersion";
import {
ACTION_ADD_SECRETS,
ACTION_DELETE_SECRETS,
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS,
ACTION_UPDATE_SECRETS,
} from '../../variables';
} from "../../variables";
/**
* Create an (audit) action for updating secrets
@ -26,7 +26,7 @@ const createActionUpdateSecret = async ({
serviceAccountId,
serviceTokenDataId,
workspaceId,
secretIds
secretIds,
}: {
name: string;
userId?: Types.ObjectId;
@ -37,11 +37,11 @@ const createActionUpdateSecret = async ({
}) => {
const latestSecretVersions = (await getLatestNSecretSecretVersionIds({
secretIds,
n: 2
n: 2,
}))
.map((s) => ({
oldSecretVersion: s.versions[0]._id,
newSecretVersion: s.versions[1]._id
newSecretVersion: s.versions[1]._id,
}));
const action = await new Action({
@ -51,8 +51,8 @@ const createActionUpdateSecret = async ({
serviceTokenData: serviceTokenDataId,
workspace: workspaceId,
payload: {
secretVersions: latestSecretVersions
}
secretVersions: latestSecretVersions,
},
}).save();
return action;
@ -72,7 +72,7 @@ const createActionSecret = async ({
serviceAccountId,
serviceTokenDataId,
workspaceId,
secretIds
secretIds,
}: {
name: string;
userId?: Types.ObjectId;
@ -84,10 +84,10 @@ const createActionSecret = async ({
// case: action is adding, deleting, or reading secrets
// -> add new secret versions
const latestSecretVersions = (await getLatestSecretVersionIds({
secretIds
secretIds,
}))
.map((s) => ({
newSecretVersion: s.versionId
newSecretVersion: s.versionId,
}));
const action = await new Action({
@ -97,8 +97,8 @@ const createActionSecret = async ({
serviceTokenData: serviceTokenDataId,
workspace: workspaceId,
payload: {
secretVersions: latestSecretVersions
}
secretVersions: latestSecretVersions,
},
}).save();
return action;
@ -116,7 +116,7 @@ const createActionClient = ({
name,
userId,
serviceAccountId,
serviceTokenDataId
serviceTokenDataId,
}: {
name: string;
userId?: Types.ObjectId;
@ -127,7 +127,7 @@ const createActionClient = ({
name,
user: userId,
serviceAccount: serviceAccountId,
serviceTokenData: serviceTokenDataId
serviceTokenData: serviceTokenDataId,
}).save();
return action;
@ -162,27 +162,27 @@ const createActionHelper = async ({
case ACTION_LOGOUT:
action = await createActionClient({
name,
userId
userId,
});
break;
case ACTION_ADD_SECRETS:
case ACTION_READ_SECRETS:
case ACTION_DELETE_SECRETS:
if (!workspaceId || !secretIds) throw new Error('Missing required params workspace id or secret ids to create action secret');
if (!workspaceId || !secretIds) throw new Error("Missing required params workspace id or secret ids to create action secret");
action = await createActionSecret({
name,
userId,
workspaceId,
secretIds
secretIds,
});
break;
case ACTION_UPDATE_SECRETS:
if (!workspaceId || !secretIds) throw new Error('Missing required params workspace id or secret ids to create action secret');
if (!workspaceId || !secretIds) throw new Error("Missing required params workspace id or secret ids to create action secret");
action = await createActionUpdateSecret({
name,
userId,
workspaceId,
secretIds
secretIds,
});
break;
}
@ -191,5 +191,5 @@ const createActionHelper = async ({
}
export {
createActionHelper
createActionHelper,
};

@ -1,7 +1,7 @@
import { Types } from 'mongoose';
import { Types } from "mongoose";
import _ from "lodash";
import { Membership } from "../../models";
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from '../../variables';
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables";
export const userHasWorkspaceAccess = async (userId: Types.ObjectId, workspaceId: Types.ObjectId, environment: string, action: any) => {
const membershipForWorkspace = await Membership.findOne({ workspace: workspaceId, user: userId })

@ -1,8 +1,8 @@
import { Types } from 'mongoose';
import { Types } from "mongoose";
import {
IAction,
Log,
IAction
} from '../models';
} from "../models";
/**
* Create an (audit) log
@ -21,7 +21,7 @@ const createLogHelper = async ({
workspaceId,
actions,
channel,
ipAddress
ipAddress,
}: {
userId?: Types.ObjectId;
serviceAccountId?: Types.ObjectId;
@ -39,12 +39,12 @@ const createLogHelper = async ({
actionNames: actions.map((a) => a.name),
actions,
channel,
ipAddress
ipAddress,
}).save();
return log;
}
export {
createLogHelper
createLogHelper,
}

@ -1,10 +1,10 @@
import { Types } from "mongoose";
import { Secret, ISecret } from "../../models";
import { Secret } from "../../models";
import {
FolderVersion,
ISecretVersion,
SecretSnapshot,
SecretVersion,
ISecretVersion,
FolderVersion,
} from "../models";
/**

@ -1,7 +1,7 @@
import requireLicenseAuth from './requireLicenseAuth';
import requireSecretSnapshotAuth from './requireSecretSnapshotAuth';
import requireLicenseAuth from "./requireLicenseAuth";
import requireSecretSnapshotAuth from "./requireSecretSnapshotAuth";
export {
requireLicenseAuth,
requireSecretSnapshotAuth
requireSecretSnapshotAuth,
}

@ -1,4 +1,4 @@
import { Request, Response, NextFunction } from 'express';
import { NextFunction, Request, Response } from "express";
/**
* Validate if organization hosting meets license requirements to
@ -7,7 +7,7 @@ import { Request, Response, NextFunction } from 'express';
* @param {String[]} obj.acceptedTiers
*/
const requireLicenseAuth = ({
acceptedTiers
acceptedTiers,
}: {
acceptedTiers: string[];
}) => {

@ -1,9 +1,9 @@
import { Request, Response, NextFunction } from 'express';
import { UnauthorizedRequestError, SecretSnapshotNotFoundError } from '../../utils/errors';
import { SecretSnapshot } from '../models';
import { NextFunction, Request, Response } from "express";
import { SecretSnapshotNotFoundError } from "../../utils/errors";
import { SecretSnapshot } from "../models";
import {
validateMembership
} from '../../helpers/membership';
validateMembership,
} from "../../helpers/membership";
/**
* Validate if user on request has proper membership for secret snapshot
@ -15,7 +15,7 @@ import {
const requireSecretSnapshotAuth = ({
acceptedRoles,
}: {
acceptedRoles: Array<'admin' | 'member'>;
acceptedRoles: Array<"admin" | "member">;
}) => {
return async (req: Request, res: Response, next: NextFunction) => {
const { secretSnapshotId } = req.params;
@ -24,14 +24,14 @@ const requireSecretSnapshotAuth = ({
if (!secretSnapshot) {
return next(SecretSnapshotNotFoundError({
message: 'Failed to find secret snapshot'
message: "Failed to find secret snapshot",
}));
}
await validateMembership({
userId: req.user._id,
workspaceId: secretSnapshot.workspace,
acceptedRoles
acceptedRoles,
});
req.secretSnapshot = secretSnapshot as any;

@ -1,12 +1,12 @@
import { Schema, model, Types } from 'mongoose';
import { Schema, Types, model } from "mongoose";
import {
ACTION_ADD_SECRETS,
ACTION_DELETE_SECRETS,
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS
} from '../../variables';
ACTION_UPDATE_SECRETS,
} from "../../variables";
export interface IAction {
name: string;
@ -30,42 +30,42 @@ const actionSchema = new Schema<IAction>(
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS
]
ACTION_DELETE_SECRETS,
],
},
user: {
type: Schema.Types.ObjectId,
ref: 'User'
ref: "User",
},
serviceAccount: {
type: Schema.Types.ObjectId,
ref: 'ServiceAccount'
ref: "ServiceAccount",
},
serviceTokenData: {
type: Schema.Types.ObjectId,
ref: 'ServiceTokenData'
ref: "ServiceTokenData",
},
workspace: {
type: Schema.Types.ObjectId,
ref: 'Workspace'
ref: "Workspace",
},
payload: {
secretVersions: [{
oldSecretVersion: {
type: Schema.Types.ObjectId,
ref: 'SecretVersion'
ref: "SecretVersion",
},
newSecretVersion: {
type: Schema.Types.ObjectId,
ref: 'SecretVersion'
}
}]
}
ref: "SecretVersion",
},
}],
},
}, {
timestamps: true
timestamps: true,
}
);
const Action = model<IAction>('Action', actionSchema);
const Action = model<IAction>("Action", actionSchema);
export default Action;

@ -1,4 +1,4 @@
import { model, Schema, Types } from "mongoose";
import { Schema, Types, model } from "mongoose";
export type TFolderRootVersionSchema = {
_id: Types.ObjectId;

@ -1,12 +1,12 @@
import { Schema, model, Types } from 'mongoose';
import { Schema, Types, model } from "mongoose";
import {
ACTION_ADD_SECRETS,
ACTION_DELETE_SECRETS,
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS
} from '../../variables';
ACTION_UPDATE_SECRETS,
} from "../../variables";
export interface ILog {
_id: Types.ObjectId;
@ -24,19 +24,19 @@ const logSchema = new Schema<ILog>(
{
user: {
type: Schema.Types.ObjectId,
ref: 'User'
ref: "User",
},
serviceAccount: {
type: Schema.Types.ObjectId,
ref: 'ServiceAccount'
ref: "ServiceAccount",
},
serviceTokenData: {
type: Schema.Types.ObjectId,
ref: 'ServiceTokenData'
ref: "ServiceTokenData",
},
workspace: {
type: Schema.Types.ObjectId,
ref: 'Workspace'
ref: "Workspace",
},
actionNames: {
type: [String],
@ -46,28 +46,28 @@ const logSchema = new Schema<ILog>(
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS
ACTION_DELETE_SECRETS,
],
required: true
required: true,
},
actions: [{
type: Schema.Types.ObjectId,
ref: 'Action',
required: true
ref: "Action",
required: true,
}],
channel: {
type: String,
enum: ['web', 'cli', 'auto', 'k8-operator', 'other'],
required: true
enum: ["web", "cli", "auto", "k8-operator", "other"],
required: true,
},
ipAddress: {
type: String
}
type: String,
},
}, {
timestamps: true
timestamps: true,
}
);
const Log = model<ILog>('Log', logSchema);
const Log = model<ILog>("Log", logSchema);
export default Log;

@ -1,4 +1,4 @@
import { Schema, model, Types } from "mongoose";
import { Schema, Types, model } from "mongoose";
export interface ISecretSnapshot {
workspace: Types.ObjectId;

@ -1,10 +1,10 @@
import { Schema, model, Types } from "mongoose";
import { Schema, Types, model } from "mongoose";
import {
SECRET_SHARED,
SECRET_PERSONAL,
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
ENCODING_SCHEME_BASE64,
ENCODING_SCHEME_UTF8,
SECRET_PERSONAL,
SECRET_SHARED,
} from "../../variables";
export interface ISecretVersion {
@ -114,9 +114,9 @@ const secretVersionSchema = new Schema<ISecretVersion>(
required: true,
},
tags: {
ref: 'Tag',
ref: "Tag",
type: [Schema.Types.ObjectId],
default: []
default: [],
},
},
{

@ -1,15 +1,15 @@
import express from 'express';
import express from "express";
const router = express.Router();
import {
validateRequest
} from '../../../middleware';
import { param } from 'express-validator';
import { actionController } from '../../controllers/v1';
validateRequest,
} from "../../../middleware";
import { param } from "express-validator";
import { actionController } from "../../controllers/v1";
// TODO: put into action controller
router.get(
'/:actionId',
param('actionId').exists().trim(),
"/:actionId",
param("actionId").exists().trim(),
validateRequest,
actionController.getAction
);

@ -1,18 +1,18 @@
import express from 'express';
import express from "express";
const router = express.Router();
import {
requireAuth,
validateRequest
} from '../../../middleware';
import { query } from 'express-validator';
import { cloudProductsController } from '../../controllers/v1';
validateRequest,
} from "../../../middleware";
import { query } from "express-validator";
import { cloudProductsController } from "../../controllers/v1";
router.get(
'/',
"/",
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
acceptedAuthModes: ["jwt", "apiKey"],
}),
query('billing-cycle').exists().isIn(['monthly', 'yearly']),
query("billing-cycle").exists().isIn(["monthly", "yearly"]),
validateRequest,
cloudProductsController.getCloudProducts
);

@ -1,9 +1,9 @@
import secret from './secret';
import secretSnapshot from './secretSnapshot';
import organizations from './organizations';
import workspace from './workspace';
import action from './action';
import cloudProducts from './cloudProducts';
import secret from "./secret";
import secretSnapshot from "./secretSnapshot";
import organizations from "./organizations";
import workspace from "./workspace";
import action from "./action";
import cloudProducts from "./cloudProducts";
export {
secret,
@ -11,5 +11,5 @@ export {
organizations,
workspace,
action,
cloudProducts
cloudProducts,
}

@ -1,86 +1,86 @@
import express from 'express';
import express from "express";
const router = express.Router();
import {
requireAuth,
requireOrganizationAuth,
validateRequest
} from '../../../middleware';
import { param, body, query } from 'express-validator';
import { organizationsController } from '../../controllers/v1';
validateRequest,
} from "../../../middleware";
import { body, param, query } from "express-validator";
import { organizationsController } from "../../controllers/v1";
import {
OWNER, ADMIN, MEMBER, ACCEPTED
} from '../../../variables';
ACCEPTED, ADMIN, MEMBER, OWNER,
} from "../../../variables";
router.get(
'/:organizationId/plan',
"/:organizationId/plan",
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
acceptedAuthModes: ["jwt", "apiKey"],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED]
acceptedStatuses: [ACCEPTED],
}),
param('organizationId').exists().trim(),
query('workspaceId').optional().isString(),
param("organizationId").exists().trim(),
query("workspaceId").optional().isString(),
validateRequest,
organizationsController.getOrganizationPlan
);
router.patch(
'/:organizationId/plan',
"/:organizationId/plan",
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
acceptedAuthModes: ["jwt", "apiKey"],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED]
acceptedStatuses: [ACCEPTED],
}),
param('organizationId').exists().trim(),
body('productId').exists().isString(),
param("organizationId").exists().trim(),
body("productId").exists().isString(),
validateRequest,
organizationsController.updateOrganizationPlan
);
router.get(
'/:organizationId/billing-details/payment-methods',
"/:organizationId/billing-details/payment-methods",
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
acceptedAuthModes: ["jwt", "apiKey"],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED]
acceptedStatuses: [ACCEPTED],
}),
param('organizationId').exists().trim(),
param("organizationId").exists().trim(),
validateRequest,
organizationsController.getOrganizationPmtMethods
);
router.post(
'/:organizationId/billing-details/payment-methods',
"/:organizationId/billing-details/payment-methods",
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
acceptedAuthModes: ["jwt", "apiKey"],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED]
acceptedStatuses: [ACCEPTED],
}),
param('organizationId').exists().trim(),
body('success_url').exists().isString(),
body('cancel_url').exists().isString(),
param("organizationId").exists().trim(),
body("success_url").exists().isString(),
body("cancel_url").exists().isString(),
validateRequest,
organizationsController.addOrganizationPmtMethod
);
router.delete(
'/:organizationId/billing-details/payment-methods/:pmtMethodId',
"/:organizationId/billing-details/payment-methods/:pmtMethodId",
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
acceptedAuthModes: ["jwt", "apiKey"],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED]
acceptedStatuses: [ACCEPTED],
}),
param('organizationId').exists().trim(),
param("organizationId").exists().trim(),
validateRequest,
organizationsController.deleteOrganizationPmtMethod
);

@ -1,46 +1,46 @@
import express from 'express';
import express from "express";
const router = express.Router();
import {
requireAuth,
requireSecretAuth,
validateRequest
} from '../../../middleware';
import { query, param, body } from 'express-validator';
import { secretController } from '../../controllers/v1';
validateRequest,
} from "../../../middleware";
import { body, param, query } from "express-validator";
import { secretController } from "../../controllers/v1";
import {
ADMIN,
MEMBER,
PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS
} from '../../../variables';
PERMISSION_WRITE_SECRETS,
} from "../../../variables";
router.get(
'/:secretId/secret-versions',
"/:secretId/secret-versions",
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
acceptedAuthModes: ["jwt", "apiKey"],
}),
requireSecretAuth({
acceptedRoles: [ADMIN, MEMBER],
requiredPermissions: [PERMISSION_READ_SECRETS]
requiredPermissions: [PERMISSION_READ_SECRETS],
}),
param('secretId').exists().trim(),
query('offset').exists().isInt(),
query('limit').exists().isInt(),
param("secretId").exists().trim(),
query("offset").exists().isInt(),
query("limit").exists().isInt(),
validateRequest,
secretController.getSecretVersions
);
router.post(
'/:secretId/secret-versions/rollback',
"/:secretId/secret-versions/rollback",
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
acceptedAuthModes: ["jwt", "apiKey"],
}),
requireSecretAuth({
acceptedRoles: [ADMIN, MEMBER],
requiredPermissions: [PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS]
requiredPermissions: [PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS],
}),
param('secretId').exists().trim(),
body('version').exists().isInt(),
param("secretId").exists().trim(),
body("version").exists().isInt(),
secretController.rollbackSecretVersion
);

@ -1,25 +1,25 @@
import express from 'express';
import express from "express";
const router = express.Router();
import {
requireSecretSnapshotAuth
} from '../../middleware';
requireSecretSnapshotAuth,
} from "../../middleware";
import {
requireAuth,
validateRequest
} from '../../../middleware';
import { param } from 'express-validator';
import { ADMIN, MEMBER } from '../../../variables';
import { secretSnapshotController } from '../../controllers/v1';
validateRequest,
} from "../../../middleware";
import { param } from "express-validator";
import { ADMIN, MEMBER } from "../../../variables";
import { secretSnapshotController } from "../../controllers/v1";
router.get(
'/:secretSnapshotId',
"/:secretSnapshotId",
requireAuth({
acceptedAuthModes: ['jwt']
acceptedAuthModes: ["jwt"],
}),
requireSecretSnapshotAuth({
acceptedRoles: [ADMIN, MEMBER]
acceptedRoles: [ADMIN, MEMBER],
}),
param('secretSnapshotId').exists().trim(),
param("secretSnapshotId").exists().trim(),
validateRequest,
secretSnapshotController.getSecretSnapshot
);

@ -1,7 +1,7 @@
import express from 'express';
import express from "express";
const router = express.Router();
import { stripeController } from '../../controllers/v1';
import { stripeController } from "../../controllers/v1";
router.post('/webhook', stripeController.handleWebhook);
router.post("/webhook", stripeController.handleWebhook);
export default router;

@ -5,7 +5,7 @@ import {
requireWorkspaceAuth,
validateRequest,
} from "../../../middleware";
import { param, query, body } from "express-validator";
import { body, param, query } from "express-validator";
import { ADMIN, MEMBER } from "../../../variables";
import { workspaceController } from "../../controllers/v1";

@ -1,22 +1,22 @@
import * as Sentry from '@sentry/node';
import NodeCache from 'node-cache';
import * as Sentry from "@sentry/node";
import NodeCache from "node-cache";
import {
getLicenseKey,
getLicenseServerKey,
getLicenseServerUrl
} from '../../config';
getLicenseServerUrl,
} from "../../config";
import {
licenseKeyRequest,
licenseServerKeyRequest,
refreshLicenseKeyToken,
refreshLicenseServerKeyToken,
refreshLicenseKeyToken
} from '../../config/request';
import { Organization } from '../../models';
import { OrganizationNotFoundError } from '../../utils/errors';
} from "../../config/request";
import { Organization } from "../../models";
import { OrganizationNotFoundError } from "../../utils/errors";
interface FeatureSet {
_id: string | null;
slug: 'starter' | 'team' | 'pro' | 'enterprise' | null;
slug: "starter" | "team" | "pro" | "enterprise" | null;
tier: number;
workspaceLimit: number | null;
workspacesUsed: number;
@ -42,7 +42,7 @@ class EELicenseService {
private readonly _isLicenseValid: boolean; // TODO: deprecate
public instanceType: 'self-hosted' | 'enterprise-self-hosted' | 'cloud' = 'self-hosted';
public instanceType: "self-hosted" | "enterprise-self-hosted" | "cloud" = "self-hosted";
public globalFeatureSet: FeatureSet = {
_id: null,
@ -59,7 +59,7 @@ class EELicenseService {
rbac: true,
customRateLimits: true,
customAlerts: true,
auditLogs: false
auditLogs: false,
}
public localFeatureSet: NodeCache;
@ -67,14 +67,14 @@ class EELicenseService {
constructor() {
this._isLicenseValid = true;
this.localFeatureSet = new NodeCache({
stdTTL: 300
stdTTL: 300,
});
}
public async getPlan(organizationId: string, workspaceId?: string): Promise<FeatureSet> {
try {
if (this.instanceType === 'cloud') {
const cachedPlan = this.localFeatureSet.get<FeatureSet>(`${organizationId}-${workspaceId ?? ''}`);
if (this.instanceType === "cloud") {
const cachedPlan = this.localFeatureSet.get<FeatureSet>(`${organizationId}-${workspaceId ?? ""}`);
if (cachedPlan) {
return cachedPlan;
}
@ -91,7 +91,7 @@ class EELicenseService {
const { data: { currentPlan } } = await licenseServerKeyRequest.get(url);
// cache fetched plan for organization
this.localFeatureSet.set(`${organizationId}-${workspaceId ?? ''}`, currentPlan);
this.localFeatureSet.set(`${organizationId}-${workspaceId ?? ""}`, currentPlan);
return currentPlan;
}
@ -103,8 +103,8 @@ class EELicenseService {
}
public async refreshPlan(organizationId: string, workspaceId?: string) {
if (this.instanceType === 'cloud') {
this.localFeatureSet.del(`${organizationId}-${workspaceId ?? ''}`);
if (this.instanceType === "cloud") {
this.localFeatureSet.del(`${organizationId}-${workspaceId ?? ""}`);
await this.getPlan(organizationId, workspaceId);
}
}
@ -119,7 +119,7 @@ class EELicenseService {
const token = await refreshLicenseServerKeyToken()
if (token) {
this.instanceType = 'cloud';
this.instanceType = "cloud";
}
return;
@ -135,7 +135,7 @@ class EELicenseService {
);
this.globalFeatureSet = currentPlan;
this.instanceType = 'enterprise-self-hosted';
this.instanceType = "enterprise-self-hosted";
}
}
} catch (err) {

@ -1,14 +1,14 @@
import { Types } from 'mongoose';
import { Types } from "mongoose";
import {
IAction
} from '../models';
IAction,
} from "../models";
import {
createLogHelper
} from '../helpers/log';
createLogHelper,
} from "../helpers/log";
import {
createActionHelper
} from '../helpers/action';
import EELicenseService from './EELicenseService';
createActionHelper,
} from "../helpers/action";
import EELicenseService from "./EELicenseService";
/**
* Class to handle Enterprise Edition log actions
@ -31,7 +31,7 @@ class EELogService {
workspaceId,
actions,
channel,
ipAddress
ipAddress,
}: {
userId?: Types.ObjectId;
serviceAccountId?: Types.ObjectId;
@ -49,7 +49,7 @@ class EELogService {
workspaceId,
actions,
channel,
ipAddress
ipAddress,
})
}
@ -68,7 +68,7 @@ class EELogService {
serviceAccountId,
serviceTokenDataId,
workspaceId,
secretIds
secretIds,
}: {
name: string;
userId?: Types.ObjectId;
@ -83,7 +83,7 @@ class EELogService {
serviceAccountId,
serviceTokenDataId,
workspaceId,
secretIds
secretIds,
});
}
}

@ -1,11 +1,11 @@
import { Types } from 'mongoose';
import { ISecretVersion } from '../models';
import { Types } from "mongoose";
import { ISecretVersion } from "../models";
import {
takeSecretSnapshotHelper,
addSecretVersionsHelper,
markDeletedSecretVersionsHelper,
} from '../helpers/secret';
import EELicenseService from './EELicenseService';
takeSecretSnapshotHelper,
} from "../helpers/secret";
import EELicenseService from "./EELicenseService";
/**
* Class to handle Enterprise Edition secret actions

@ -5,5 +5,5 @@ import EELogService from "./EELogService";
export {
EELicenseService,
EESecretService,
EELogService
EELogService,
}

@ -1,5 +1,5 @@
import { eventPushSecrets } from "./secret"
export {
eventPushSecrets
eventPushSecrets,
}

@ -1,8 +1,8 @@
import { Types } from 'mongoose';
import { Types } from "mongoose";
import {
EVENT_PULL_SECRETS,
EVENT_PUSH_SECRETS,
EVENT_PULL_SECRETS
} from '../variables';
} from "../variables";
interface PushSecret {
ciphertextKey: string;
@ -13,7 +13,7 @@ interface PushSecret {
ivValue: string;
tagValue: string;
hashValue: string;
type: 'shared' | 'personal';
type: "shared" | "personal";
}
/**
@ -24,7 +24,7 @@ interface PushSecret {
*/
const eventPushSecrets = ({
workspaceId,
environment
environment,
}: {
workspaceId: Types.ObjectId;
environment?: string;
@ -35,7 +35,7 @@ const eventPushSecrets = ({
environment,
payload: {
}
},
});
}
@ -55,10 +55,10 @@ const eventPullSecrets = ({
workspaceId,
payload: {
}
},
});
}
export {
eventPushSecrets
eventPushSecrets,
}

@ -1,36 +1,36 @@
import { Types } from 'mongoose';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
import { Types } from "mongoose";
import jwt from "jsonwebtoken";
import bcrypt from "bcrypt";
import {
IUser,
User,
ServiceTokenData,
ServiceAccount,
APIKeyData,
ITokenVersion,
IUser,
ServiceAccount,
ServiceTokenData,
TokenVersion,
ITokenVersion
} from '../models';
User,
} from "../models";
import {
AccountNotFoundError,
ServiceTokenDataNotFoundError,
ServiceAccountNotFoundError,
APIKeyDataNotFoundError,
AccountNotFoundError,
BadRequestError,
ServiceAccountNotFoundError,
ServiceTokenDataNotFoundError,
UnauthorizedRequestError,
BadRequestError
} from '../utils/errors';
} from "../utils/errors";
import {
getJwtAuthLifetime,
getJwtAuthSecret,
getJwtProviderAuthSecret,
getJwtRefreshLifetime,
getJwtRefreshSecret
} from '../config';
getJwtRefreshSecret,
} from "../config";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_API_KEY
} from '../variables';
} from "../variables";
/**
*
@ -39,41 +39,41 @@ import {
*/
export const validateAuthMode = ({
headers,
acceptedAuthModes
acceptedAuthModes,
}: {
headers: { [key: string]: string | string[] | undefined },
acceptedAuthModes: string[]
}) => {
const apiKey = headers['x-api-key'];
const authHeader = headers['authorization'];
const apiKey = headers["x-api-key"];
const authHeader = headers["authorization"];
let authMode, authTokenValue;
if (apiKey === undefined && authHeader === undefined) {
// case: no auth or X-API-KEY header present
throw BadRequestError({ message: 'Missing Authorization or X-API-KEY in request header.' });
throw BadRequestError({ message: "Missing Authorization or X-API-KEY in request header." });
}
if (typeof apiKey === 'string') {
if (typeof apiKey === "string") {
// case: treat request authentication type as via X-API-KEY (i.e. API Key)
authMode = AUTH_MODE_API_KEY;
authTokenValue = apiKey;
}
if (typeof authHeader === 'string') {
if (typeof authHeader === "string") {
// case: treat request authentication type as via Authorization header (i.e. either JWT or service token)
const [tokenType, tokenValue] = <[string, string]>authHeader.split(' ', 2) ?? [null, null]
const [tokenType, tokenValue] = <[string, string]>authHeader.split(" ", 2) ?? [null, null]
if (tokenType === null)
throw BadRequestError({ message: `Missing Authorization Header in the request header.` });
if (tokenType.toLowerCase() !== 'bearer')
throw BadRequestError({ message: "Missing Authorization Header in the request header." });
if (tokenType.toLowerCase() !== "bearer")
throw BadRequestError({ message: `The provided authentication type '${tokenType}' is not supported.` });
if (tokenValue === null)
throw BadRequestError({ message: 'Missing Authorization Body in the request header.' });
throw BadRequestError({ message: "Missing Authorization Body in the request header." });
switch (tokenValue.split('.', 1)[0]) {
case 'st':
switch (tokenValue.split(".", 1)[0]) {
case "st":
authMode = AUTH_MODE_SERVICE_TOKEN;
break;
case 'sa':
case "sa":
authMode = AUTH_MODE_SERVICE_ACCOUNT;
break;
default:
@ -83,13 +83,13 @@ export const validateAuthMode = ({
authTokenValue = tokenValue;
}
if (!authMode || !authTokenValue) throw BadRequestError({ message: 'Missing valid Authorization or X-API-KEY in request header.' });
if (!authMode || !authTokenValue) throw BadRequestError({ message: "Missing valid Authorization or X-API-KEY in request header." });
if (!acceptedAuthModes.includes(authMode)) throw BadRequestError({ message: 'The provided authentication type is not supported.' });
if (!acceptedAuthModes.includes(authMode)) throw BadRequestError({ message: "The provided authentication type is not supported." });
return ({
authMode,
authTokenValue
authTokenValue,
});
}
@ -100,7 +100,7 @@ export const validateAuthMode = ({
* @returns {User} user - user corresponding to JWT token
*/
export const getAuthUserPayload = async ({
authTokenValue
authTokenValue,
}: {
authTokenValue: string;
}) => {
@ -109,31 +109,31 @@ export const getAuthUserPayload = async ({
);
const user = await User.findOne({
_id: new Types.ObjectId(decodedToken.userId)
}).select('+publicKey +accessVersion');
_id: new Types.ObjectId(decodedToken.userId),
}).select("+publicKey +accessVersion");
if (!user) throw AccountNotFoundError({ message: 'Failed to find user' });
if (!user) throw AccountNotFoundError({ message: "Failed to find user" });
if (!user?.publicKey) throw UnauthorizedRequestError({ message: 'Failed to authenticate user with partially set up account' });
if (!user?.publicKey) throw UnauthorizedRequestError({ message: "Failed to authenticate user with partially set up account" });
const tokenVersion = await TokenVersion.findOneAndUpdate({
_id: new Types.ObjectId(decodedToken.tokenVersionId),
user: user._id
user: user._id,
}, {
lastUsed: new Date()
lastUsed: new Date(),
});
if (!tokenVersion) throw UnauthorizedRequestError({
message: 'Failed to validate access token'
message: "Failed to validate access token",
});
if (decodedToken.accessVersion !== tokenVersion.accessVersion) throw UnauthorizedRequestError({
message: 'Failed to validate access token'
message: "Failed to validate access token",
});
return ({
user,
tokenVersionId: tokenVersion._id
tokenVersionId: tokenVersion._id,
});
}
@ -144,41 +144,41 @@ export const getAuthUserPayload = async ({
* @returns {ServiceTokenData} serviceTokenData - service token data
*/
export const getAuthSTDPayload = async ({
authTokenValue
authTokenValue,
}: {
authTokenValue: string;
}) => {
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
let serviceTokenData = await ServiceTokenData
.findById(TOKEN_IDENTIFIER, '+secretHash +expiresAt');
.findById(TOKEN_IDENTIFIER, "+secretHash +expiresAt");
if (!serviceTokenData) {
throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
throw ServiceTokenDataNotFoundError({ message: "Failed to find service token data" });
} else if (serviceTokenData?.expiresAt && new Date(serviceTokenData.expiresAt) < new Date()) {
// case: service token expired
await ServiceTokenData.findByIdAndDelete(serviceTokenData._id);
throw UnauthorizedRequestError({
message: 'Failed to authenticate expired service token'
message: "Failed to authenticate expired service token",
});
}
const isMatch = await bcrypt.compare(TOKEN_SECRET, serviceTokenData.secretHash);
if (!isMatch) throw UnauthorizedRequestError({
message: 'Failed to authenticate service token'
message: "Failed to authenticate service token",
});
serviceTokenData = await ServiceTokenData
.findOneAndUpdate({
_id: new Types.ObjectId(TOKEN_IDENTIFIER)
_id: new Types.ObjectId(TOKEN_IDENTIFIER),
}, {
lastUsed: new Date()
lastUsed: new Date(),
}, {
new: true
new: true,
})
.select('+encryptedKey +iv +tag');
.select("+encryptedKey +iv +tag");
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({ message: "Failed to find service token data" });
return serviceTokenData;
}
@ -190,23 +190,23 @@ export const getAuthSTDPayload = async ({
* @returns {ServiceAccount} serviceAccount
*/
export const getAuthSAAKPayload = async ({
authTokenValue
authTokenValue,
}: {
authTokenValue: string;
}) => {
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
const serviceAccount = await ServiceAccount.findById(
Buffer.from(TOKEN_IDENTIFIER, 'base64').toString('hex')
).select('+secretHash');
Buffer.from(TOKEN_IDENTIFIER, "base64").toString("hex")
).select("+secretHash");
if (!serviceAccount) {
throw ServiceAccountNotFoundError({ message: 'Failed to find service account' });
throw ServiceAccountNotFoundError({ message: "Failed to find service account" });
}
const result = await bcrypt.compare(TOKEN_SECRET, serviceAccount.secretHash);
if (!result) throw UnauthorizedRequestError({
message: 'Failed to authenticate service account access key'
message: "Failed to authenticate service account access key",
});
return serviceAccount;
@ -219,48 +219,48 @@ export const getAuthSAAKPayload = async ({
* @returns {APIKeyData} apiKeyData - API key data
*/
export const getAuthAPIKeyPayload = async ({
authTokenValue
authTokenValue,
}: {
authTokenValue: string;
}) => {
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
let apiKeyData = await APIKeyData
.findById(TOKEN_IDENTIFIER, '+secretHash +expiresAt')
.populate<{ user: IUser }>('user', '+publicKey');
.findById(TOKEN_IDENTIFIER, "+secretHash +expiresAt")
.populate<{ user: IUser }>("user", "+publicKey");
if (!apiKeyData) {
throw APIKeyDataNotFoundError({ message: 'Failed to find API key data' });
throw APIKeyDataNotFoundError({ message: "Failed to find API key data" });
} else if (apiKeyData?.expiresAt && new Date(apiKeyData.expiresAt) < new Date()) {
// case: API key expired
await APIKeyData.findByIdAndDelete(apiKeyData._id);
throw UnauthorizedRequestError({
message: 'Failed to authenticate expired API key'
message: "Failed to authenticate expired API key",
});
}
const isMatch = await bcrypt.compare(TOKEN_SECRET, apiKeyData.secretHash);
if (!isMatch) throw UnauthorizedRequestError({
message: 'Failed to authenticate API key'
message: "Failed to authenticate API key",
});
apiKeyData = await APIKeyData.findOneAndUpdate({
_id: new Types.ObjectId(TOKEN_IDENTIFIER)
_id: new Types.ObjectId(TOKEN_IDENTIFIER),
}, {
lastUsed: new Date()
lastUsed: new Date(),
}, {
new: true
new: true,
});
if (!apiKeyData) {
throw APIKeyDataNotFoundError({ message: 'Failed to find API key data' });
throw APIKeyDataNotFoundError({ message: "Failed to find API key data" });
}
const user = await User.findById(apiKeyData.user).select('+publicKey');
const user = await User.findById(apiKeyData.user).select("+publicKey");
if (!user) {
throw AccountNotFoundError({
message: 'Failed to find user'
message: "Failed to find user",
});
}
@ -278,7 +278,7 @@ export const getAuthAPIKeyPayload = async ({
export const issueAuthTokens = async ({
userId,
ip,
userAgent
userAgent,
}: {
userId: Types.ObjectId;
ip: string;
@ -290,7 +290,7 @@ export const issueAuthTokens = async ({
tokenVersion = await TokenVersion.findOne({
user: userId,
ip,
userAgent
userAgent,
});
if (!tokenVersion) {
@ -302,7 +302,7 @@ export const issueAuthTokens = async ({
accessVersion: 0,
ip,
userAgent,
lastUsed: new Date()
lastUsed: new Date(),
}).save();
}
@ -311,25 +311,25 @@ export const issueAuthTokens = async ({
payload: {
userId,
tokenVersionId: tokenVersion._id.toString(),
accessVersion: tokenVersion.accessVersion
accessVersion: tokenVersion.accessVersion,
},
expiresIn: await getJwtAuthLifetime(),
secret: await getJwtAuthSecret()
secret: await getJwtAuthSecret(),
});
const refreshToken = createToken({
payload: {
userId,
tokenVersionId: tokenVersion._id.toString(),
refreshVersion: tokenVersion.refreshVersion
refreshVersion: tokenVersion.refreshVersion,
},
expiresIn: await getJwtRefreshLifetime(),
secret: await getJwtRefreshSecret()
secret: await getJwtRefreshSecret(),
});
return {
token,
refreshToken
refreshToken,
};
};
@ -342,12 +342,12 @@ export const clearTokens = async (tokenVersionId: Types.ObjectId): Promise<void>
// increment refreshVersion on user by 1
await TokenVersion.findOneAndUpdate({
_id: tokenVersionId
_id: tokenVersionId,
}, {
$inc: {
refreshVersion: 1,
accessVersion: 1
}
accessVersion: 1,
},
});
};
@ -362,14 +362,14 @@ export const clearTokens = async (tokenVersionId: Types.ObjectId): Promise<void>
export const createToken = ({
payload,
expiresIn,
secret
secret,
}: {
payload: any;
expiresIn: string | number;
secret: string;
}) => {
return jwt.sign(payload, secret, {
expiresIn
expiresIn,
});
};
@ -383,7 +383,7 @@ export const validateProviderAuthToken = async ({
providerAuthToken?: string;
}) => {
if (!providerAuthToken) {
throw new Error('Invalid authentication request.');
throw new Error("Invalid authentication request.");
}
const decodedToken = <jwt.ProviderAuthJwtPayload>(
@ -394,6 +394,6 @@ export const validateProviderAuthToken = async ({
decodedToken.authProvider !== user.authProvider ||
decodedToken.email !== email
) {
throw new Error('Invalid authentication credentials.')
throw new Error("Invalid authentication credentials.")
}
}

@ -1,18 +1,18 @@
import { Types } from "mongoose";
import { Bot, BotKey, Secret, ISecret, IUser } from "../models";
import { Bot, BotKey, ISecret, IUser, Secret } from "../models";
import {
generateKeyPair,
encryptSymmetric128BitHexKeyUTF8,
decryptSymmetric128BitHexKeyUTF8,
decryptAsymmetric,
decryptSymmetric128BitHexKeyUTF8,
encryptSymmetric128BitHexKeyUTF8,
generateKeyPair,
} from "../utils/crypto";
import {
SECRET_SHARED,
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
ENCODING_SCHEME_BASE64,
ENCODING_SCHEME_UTF8,
SECRET_SHARED,
} from "../variables";
import { getEncryptionKey, getRootEncryptionKey, client } from "../config";
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
import { InternalServerError } from "../utils/errors";
import Folder from "../models/folder";
import { getFolderByPath } from "../services/FolderService";

@ -1,5 +1,5 @@
import mongoose from 'mongoose';
import { getLogger } from '../utils/logger';
import mongoose from "mongoose";
import { getLogger } from "../utils/logger";
/**
* Initialize database connection
@ -8,7 +8,7 @@ import { getLogger } from '../utils/logger';
* @returns
*/
export const initDatabaseHelper = async ({
mongoURL
mongoURL,
}: {
mongoURL: string;
}) => {
@ -16,7 +16,7 @@ export const initDatabaseHelper = async ({
await mongoose.connect(mongoURL);
// allow empty strings to pass the required validator
mongoose.Schema.Types.String.checkRequired(v => typeof v === 'string');
mongoose.Schema.Types.String.checkRequired(v => typeof v === "string");
(await getLogger("database")).info("Database connection established");
@ -35,10 +35,10 @@ export const closeDatabaseHelper = async () => {
new Promise((resolve) => {
if (mongoose.connection && mongoose.connection.readyState == 1) {
mongoose.connection.close()
.then(() => resolve('Database connection closed'));
.then(() => resolve("Database connection closed"));
} else {
resolve('Database connection already closed');
resolve("Database connection already closed");
}
})
}),
]);
}

@ -1,5 +1,5 @@
import { Types } from "mongoose";
import { Bot, IBot } from "../models";
import { Bot } from "../models";
import { EVENT_PUSH_SECRETS } from "../variables";
import { IntegrationService } from "../services";

@ -1,17 +1,17 @@
export * from './auth';
export * from './bot';
export * from './database';
export * from './event';
export * from './integration';
export * from './key';
export * from './membership';
export * from './membershipOrg';
export * from './nodemailer';
export * from './organization';
export * from './rateLimiter';
export * from './secret';
export * from './secrets';
export * from './signup';
export * from './token';
export * from './user';
export * from './workspace';
export * from "./auth";
export * from "./bot";
export * from "./database";
export * from "./event";
export * from "./integration";
export * from "./key";
export * from "./membership";
export * from "./membershipOrg";
export * from "./nodemailer";
export * from "./organization";
export * from "./rateLimiter";
export * from "./secret";
export * from "./secrets";
export * from "./signup";
export * from "./token";
export * from "./user";
export * from "./workspace";

@ -3,10 +3,10 @@ import { Bot, Integration, IntegrationAuth } from "../models";
import { exchangeCode, exchangeRefresh, syncSecrets } from "../integrations";
import { BotService } from "../services";
import {
INTEGRATION_VERCEL,
INTEGRATION_NETLIFY,
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
INTEGRATION_NETLIFY,
INTEGRATION_VERCEL,
} from "../variables";
import { UnauthorizedRequestError } from "../utils/errors";

@ -1,4 +1,4 @@
import { Key, IKey } from '../models';
import { IKey, Key } from "../models";
interface Key {
encryptedKey: string;
@ -20,7 +20,7 @@ interface Key {
export const pushKeys = async ({
userId,
workspaceId,
keys
keys,
}: {
userId: string;
workspaceId: string;
@ -31,9 +31,9 @@ export const pushKeys = async ({
(
await Key.find(
{
workspace: workspaceId
workspace: workspaceId,
},
'receiver'
"receiver"
)
).map((k: IKey) => k.receiver.toString())
);
@ -47,7 +47,7 @@ export const pushKeys = async ({
nonce: k.nonce,
sender: userId,
receiver: k.userId,
workspace: workspaceId
workspace: workspaceId,
}))
);
};

@ -1,6 +1,6 @@
import { Types } from "mongoose";
import { Membership, Key } from "../models";
import { MembershipNotFoundError, BadRequestError } from "../utils/errors";
import { Key, Membership } from "../models";
import { BadRequestError, MembershipNotFoundError } from "../utils/errors";
/**
* Validate that user with id [userId] is a member of workspace with id [workspaceId]
@ -62,7 +62,7 @@ export const findMembership = async (queryObj: any) => {
export const addMemberships = async ({
userIds,
workspaceId,
roles
roles,
}: {
userIds: string[];
workspaceId: string;
@ -95,7 +95,7 @@ export const addMemberships = async ({
*/
export const deleteMembership = async ({ membershipId }: { membershipId: string }) => {
const deletedMembership = await Membership.findOneAndDelete({
_id: membershipId
_id: membershipId,
});
// delete keys associated with the membership

@ -1,14 +1,14 @@
import { Types } from 'mongoose';
import { Types } from "mongoose";
import {
MembershipOrg,
Workspace,
Key,
Membership,
Key
} from '../models';
MembershipOrg,
Workspace,
} from "../models";
import {
MembershipOrgNotFoundError,
UnauthorizedRequestError
} from '../utils/errors';
UnauthorizedRequestError,
} from "../utils/errors";
/**
* Validate that user with id [userId] is a member of organization with id [organizationId]
@ -22,31 +22,31 @@ export const validateMembershipOrg = async ({
userId,
organizationId,
acceptedRoles,
acceptedStatuses
acceptedStatuses,
}: {
userId: Types.ObjectId;
organizationId: Types.ObjectId;
acceptedRoles?: Array<'owner' | 'admin' | 'member'>;
acceptedStatuses?: Array<'invited' | 'accepted'>;
acceptedRoles?: Array<"owner" | "admin" | "member">;
acceptedStatuses?: Array<"invited" | "accepted">;
}) => {
const membershipOrg = await MembershipOrg.findOne({
user: userId,
organization: organizationId
organization: organizationId,
});
if (!membershipOrg) {
throw MembershipOrgNotFoundError({ message: 'Failed to find organization membership' });
throw MembershipOrgNotFoundError({ message: "Failed to find organization membership" });
}
if (acceptedRoles) {
if (!acceptedRoles.includes(membershipOrg.role)) {
throw UnauthorizedRequestError({ message: 'Failed to validate organization membership role' });
throw UnauthorizedRequestError({ message: "Failed to validate organization membership role" });
}
}
if (acceptedStatuses) {
if (!acceptedStatuses.includes(membershipOrg.status)) {
throw UnauthorizedRequestError({ message: 'Failed to validate organization membership status' });
throw UnauthorizedRequestError({ message: "Failed to validate organization membership status" });
}
}
@ -76,7 +76,7 @@ export const addMembershipsOrg = async ({
userIds,
organizationId,
roles,
statuses
statuses,
}: {
userIds: string[];
organizationId: string;
@ -90,16 +90,16 @@ export const addMembershipsOrg = async ({
user: userId,
organization: organizationId,
role: roles[idx],
status: statuses[idx]
status: statuses[idx],
},
update: {
user: userId,
organization: organizationId,
role: roles[idx],
status: statuses[idx]
status: statuses[idx],
},
upsert: true
}
upsert: true,
},
};
});
@ -112,15 +112,15 @@ export const addMembershipsOrg = async ({
* @param {String} obj.membershipOrgId - id of organization membership to delete
*/
export const deleteMembershipOrg = async ({
membershipOrgId
membershipOrgId,
}: {
membershipOrgId: string;
}) => {
const deletedMembershipOrg = await MembershipOrg.findOneAndDelete({
_id: membershipOrgId
_id: membershipOrgId,
});
if (!deletedMembershipOrg) throw new Error('Failed to delete organization membership');
if (!deletedMembershipOrg) throw new Error("Failed to delete organization membership");
// delete keys associated with organization membership
if (deletedMembershipOrg?.user) {
@ -128,22 +128,22 @@ export const deleteMembershipOrg = async ({
const workspaces = (
await Workspace.find({
organization: deletedMembershipOrg.organization
organization: deletedMembershipOrg.organization,
})
).map((w) => w._id.toString());
await Membership.deleteMany({
user: deletedMembershipOrg.user,
workspace: {
$in: workspaces
}
$in: workspaces,
},
});
await Key.deleteMany({
receiver: deletedMembershipOrg.user,
workspace: {
$in: workspaces
}
$in: workspaces,
},
});
}

@ -1,8 +1,8 @@
import fs from 'fs';
import path from 'path';
import handlebars from 'handlebars';
import nodemailer from 'nodemailer';
import { getSmtpFromName, getSmtpFromAddress, getSmtpConfigured } from '../config';
import fs from "fs";
import path from "path";
import handlebars from "handlebars";
import nodemailer from "nodemailer";
import { getSmtpConfigured, getSmtpFromAddress, getSmtpFromName } from "../config";
let smtpTransporter: nodemailer.Transporter;
@ -17,7 +17,7 @@ export const sendMail = async ({
template,
subjectLine,
recipients,
substitutions
substitutions,
}: {
template: string;
subjectLine: string;
@ -26,17 +26,17 @@ export const sendMail = async ({
}) => {
if (await getSmtpConfigured()) {
const html = fs.readFileSync(
path.resolve(__dirname, '../templates/' + template),
'utf8'
path.resolve(__dirname, "../templates/" + template),
"utf8"
);
const temp = handlebars.compile(html);
const htmlToSend = temp(substitutions);
await smtpTransporter.sendMail({
from: `"${await getSmtpFromName()}" <${await getSmtpFromAddress()}>`,
to: recipients.join(', '),
to: recipients.join(", "),
subject: subjectLine,
html: htmlToSend
html: htmlToSend,
});
}
};

@ -1,25 +1,25 @@
import Stripe from "stripe";
import { Types } from "mongoose";
import { Organization, MembershipOrg } from "../models";
import { MembershipOrg, Organization } from "../models";
import {
ACCEPTED
ACCEPTED,
} from "../variables";
import {
getStripeSecretKey,
getStripeProductPro,
getStripeProductTeam,
getStripeProductStarter,
getStripeProductTeam,
getStripeSecretKey,
} from "../config";
import {
EELicenseService
} from '../ee/services';
EELicenseService,
} from "../ee/services";
import {
getLicenseServerUrl
} from '../config';
getLicenseServerUrl,
} from "../config";
import {
licenseKeyRequest,
licenseServerKeyRequest,
licenseKeyRequest
} from '../config/request';
} from "../config/request";
/**
* Create an organization with name [name]
@ -137,7 +137,7 @@ export const updateSubscriptionOrgQuantity = async ({
});
if (organization && organization.customerId) {
if (EELicenseService.instanceType === 'cloud') {
if (EELicenseService.instanceType === "cloud") {
// instance of Infisical is a cloud instance
const quantity = await MembershipOrg.countDocuments({
organization: new Types.ObjectId(organizationId),
@ -147,7 +147,7 @@ export const updateSubscriptionOrgQuantity = async ({
await licenseServerKeyRequest.patch(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${organization.customerId}/cloud-plan`,
{
quantity
quantity,
}
);
@ -155,17 +155,17 @@ export const updateSubscriptionOrgQuantity = async ({
}
}
if (EELicenseService.instanceType === 'enterprise-self-hosted') {
if (EELicenseService.instanceType === "enterprise-self-hosted") {
// instance of Infisical is an enterprise self-hosted instance
const usedSeats = await MembershipOrg.countDocuments({
status: ACCEPTED
status: ACCEPTED,
});
await licenseKeyRequest.patch(
`${await getLicenseServerUrl()}/api/license/v1/license`,
{
usedSeats
usedSeats,
}
);
}

@ -1,4 +1,4 @@
import rateLimit from 'express-rate-limit';
import rateLimit from "express-rate-limit";
// const MongoStore = require('rate-limit-mongo');
// 200 per minute
@ -14,11 +14,11 @@ export const apiLimiter = rateLimit({
standardHeaders: true,
legacyHeaders: false,
skip: (request) => {
return request.path === '/healthcheck' || request.path === '/api/status'
return request.path === "/healthcheck" || request.path === "/api/status"
},
keyGenerator: (req, res) => {
return req.realIP
}
},
});
// 50 requests per 1 hours
@ -35,7 +35,7 @@ const authLimit = rateLimit({
legacyHeaders: false,
keyGenerator: (req, res) => {
return req.realIP
}
},
});
// 5 requests per 1 hour
@ -52,11 +52,11 @@ export const passwordLimiter = rateLimit({
legacyHeaders: false,
keyGenerator: (req, res) => {
return req.realIP
}
},
});
export const authLimiter = (req: any, res: any, next: any) => {
if (process.env.NODE_ENV === 'production') {
if (process.env.NODE_ENV === "production") {
authLimit(req, res, next);
} else {
next();

@ -1,16 +1,16 @@
import { Types } from "mongoose";
import { Secret, ISecret } from "../models";
import { EESecretService, EELogService } from "../ee/services";
import { ISecret, Secret } from "../models";
import { EELogService, EESecretService } from "../ee/services";
import { IAction, SecretVersion } from "../ee/models";
import {
SECRET_SHARED,
SECRET_PERSONAL,
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_DELETE_SECRETS,
ACTION_READ_SECRETS,
ACTION_UPDATE_SECRETS,
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
SECRET_PERSONAL,
SECRET_SHARED,
} from "../variables";
interface V1PushSecret {

@ -1,45 +1,45 @@
import { Types } from "mongoose";
import {
CreateSecretParams,
GetSecretsParams,
GetSecretParams,
UpdateSecretParams,
DeleteSecretParams,
} from '../interfaces/services/SecretService';
GetSecretParams,
GetSecretsParams,
UpdateSecretParams,
} from "../interfaces/services/SecretService";
import {
Secret,
ISecret,
Secret,
SecretBlindIndexData,
ServiceTokenData,
} from "../models";
import { SecretVersion } from "../ee/models";
import {
BadRequestError,
SecretNotFoundError,
SecretBlindIndexDataNotFoundError,
InternalServerError,
SecretBlindIndexDataNotFoundError,
SecretNotFoundError,
UnauthorizedRequestError,
} from "../utils/errors";
import {
SECRET_PERSONAL,
SECRET_SHARED,
ACTION_ADD_SECRETS,
ACTION_DELETE_SECRETS,
ACTION_READ_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_DELETE_SECRETS,
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
ENCODING_SCHEME_BASE64,
ENCODING_SCHEME_UTF8,
SECRET_PERSONAL,
SECRET_SHARED,
} from "../variables";
import crypto from "crypto";
import * as argon2 from "argon2";
import {
encryptSymmetric128BitHexKeyUTF8,
decryptSymmetric128BitHexKeyUTF8,
} from '../utils/crypto';
import { TelemetryService } from '../services';
import { getEncryptionKey, client, getRootEncryptionKey } from "../config";
import { EESecretService, EELogService } from "../ee/services";
encryptSymmetric128BitHexKeyUTF8,
} from "../utils/crypto";
import { TelemetryService } from "../services";
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
import { EELogService, EESecretService } from "../ee/services";
import {
getAuthDataPayloadIdObj,
getAuthDataPayloadUserObj,
@ -56,7 +56,7 @@ import { getFolderIdFromServiceToken } from "../services/FolderService";
*/
export const repackageSecretToRaw = ({
secret,
key
key,
}: {
secret: ISecret;
key: string;
@ -66,24 +66,24 @@ export const repackageSecretToRaw = ({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key
key,
});
const secretValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key
key,
});
let secretComment: string = '';
let secretComment = "";
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
secretComment = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretCommentCiphertext,
iv: secret.secretCommentIV,
tag: secret.secretCommentTag,
key
key,
});
}
@ -96,7 +96,7 @@ export const repackageSecretToRaw = ({
user: secret.user,
secretKey,
secretValue,
secretComment
secretComment,
});
}
@ -944,6 +944,6 @@ export const deleteSecretHelper = async ({
return ({
secrets,
secret
secret,
});
};

@ -1,10 +1,10 @@
import { IUser } from '../models';
import { createOrganization } from './organization';
import { addMembershipsOrg } from './membershipOrg';
import { OWNER, ACCEPTED } from '../variables';
import { sendMail } from '../helpers/nodemailer';
import { TokenService } from '../services';
import { TOKEN_EMAIL_CONFIRMATION } from '../variables';
import { IUser } from "../models";
import { createOrganization } from "./organization";
import { addMembershipsOrg } from "./membershipOrg";
import { ACCEPTED, OWNER } from "../variables";
import { sendMail } from "../helpers/nodemailer";
import { TokenService } from "../services";
import { TOKEN_EMAIL_CONFIRMATION } from "../variables";
/**
* Send magic link to verify email to [email]
@ -16,17 +16,17 @@ import { TOKEN_EMAIL_CONFIRMATION } from '../variables';
export const sendEmailVerification = async ({ email }: { email: string }) => {
const token = await TokenService.createToken({
type: TOKEN_EMAIL_CONFIRMATION,
email
email,
});
// send mail
await sendMail({
template: 'emailVerification.handlebars',
subjectLine: 'Infisical confirmation code',
template: "emailVerification.handlebars",
subjectLine: "Infisical confirmation code",
recipients: [email],
substitutions: {
code: token
}
code: token,
},
});
};
@ -38,7 +38,7 @@ export const sendEmailVerification = async ({ email }: { email: string }) => {
*/
export const checkEmailVerification = async ({
email,
code
code,
}: {
email: string;
code: string;
@ -46,7 +46,7 @@ export const checkEmailVerification = async ({
await TokenService.validateToken({
type: TOKEN_EMAIL_CONFIRMATION,
email,
token: code
token: code,
});
};
@ -59,7 +59,7 @@ export const checkEmailVerification = async ({
*/
export const initializeDefaultOrg = async ({
organizationName,
user
user,
}: {
organizationName: string;
user: IUser;
@ -69,14 +69,14 @@ export const initializeDefaultOrg = async ({
// subscription
const organization = await createOrganization({
email: user.email,
name: organizationName
name: organizationName,
});
await addMembershipsOrg({
userIds: [user._id.toString()],
organizationId: organization._id.toString(),
roles: [OWNER],
statuses: [ACCEPTED]
statuses: [ACCEPTED],
});
} catch (err) {
throw new Error(`Failed to initialize default organization and workspace [err=${err}]`);

@ -1,8 +1,8 @@
import {
IUser,
User,
} from '../models';
import { sendMail } from './nodemailer';
} from "../models";
import { sendMail } from "./nodemailer";
/**
* Initialize a user under email [email]
@ -12,7 +12,7 @@ import { sendMail } from './nodemailer';
*/
export const setupAccount = async ({ email }: { email: string }) => {
const user = await new User({
email
email,
}).save();
return user;
@ -49,7 +49,7 @@ export const completeAccount = async ({
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
verifier,
}: {
userId: string;
firstName: string;
@ -66,7 +66,7 @@ export const completeAccount = async ({
verifier: string;
}) => {
const options = {
new: true
new: true,
};
const user = await User.findByIdAndUpdate(
userId,
@ -82,7 +82,7 @@ export const completeAccount = async ({
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag,
salt,
verifier
verifier,
},
options
);
@ -100,7 +100,7 @@ export const completeAccount = async ({
export const checkUserDevice = async ({
user,
ip,
userAgent
userAgent,
}: {
user: IUser;
ip: string;
@ -114,22 +114,22 @@ export const checkUserDevice = async ({
user.devices = user.devices.concat([{
ip: String(ip),
userAgent
userAgent,
}]);
await user.save();
// send MFA code [code] to [email]
await sendMail({
template: 'newDevice.handlebars',
subjectLine: `Successful login from new device`,
template: "newDevice.handlebars",
subjectLine: "Successful login from new device",
recipients: [user.email],
substitutions: {
email: user.email,
timestamp: new Date().toString(),
ip,
userAgent
}
userAgent,
},
});
}
}

@ -1,13 +1,13 @@
import {
Workspace,
Bot,
Membership,
Key,
Secret
} from '../models';
import { createBot } from '../helpers/bot';
import { EELicenseService } from '../ee/services';
import { SecretService } from '../services';
Membership,
Secret,
Workspace,
} from "../models";
import { createBot } from "../helpers/bot";
import { EELicenseService } from "../ee/services";
import { SecretService } from "../services";
/**
* Create a workspace with name [name] in organization with id [organizationId]
@ -18,7 +18,7 @@ import { SecretService } from '../services';
*/
export const createWorkspace = async ({
name,
organizationId
organizationId,
}: {
name: string;
organizationId: string;
@ -27,18 +27,18 @@ export const createWorkspace = async ({
const workspace = await new Workspace({
name,
organization: organizationId,
autoCapitalization: true
autoCapitalization: true,
}).save();
// initialize bot for workspace
await createBot({
name: 'Infisical Bot',
workspaceId: workspace._id
name: "Infisical Bot",
workspaceId: workspace._id,
});
// initialize blind index salt for workspace
await SecretService.createSecretBlindIndexData({
workspaceId: workspace._id
workspaceId: workspace._id,
});
await EELicenseService.refreshPlan(organizationId);
@ -55,15 +55,15 @@ export const createWorkspace = async ({
export const deleteWorkspace = async ({ id }: { id: string }) => {
await Workspace.deleteOne({ _id: id });
await Bot.deleteOne({
workspace: id
workspace: id,
});
await Membership.deleteMany({
workspace: id
workspace: id,
});
await Secret.deleteMany({
workspace: id
workspace: id,
});
await Key.deleteMany({
workspace: id
workspace: id,
});
};

@ -2,7 +2,7 @@ import dotenv from "dotenv";
dotenv.config();
import express from "express";
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('express-async-errors');
require("express-async-errors");
import helmet from "helmet";
import cors from "cors";
import { DatabaseService } from "./services";
@ -15,32 +15,32 @@ const swaggerFile = require("../spec.json");
// eslint-disable-next-line @typescript-eslint/no-var-requires
import { apiLimiter } from "./helpers/rateLimiter";
import {
workspace as eeWorkspaceRouter,
action as eeActionRouter,
cloudProducts as eeCloudProductsRouter,
organizations as eeOrganizationsRouter,
secret as eeSecretRouter,
secretSnapshot as eeSecretSnapshotRouter,
action as eeActionRouter,
organizations as eeOrganizationsRouter,
cloudProducts as eeCloudProductsRouter,
workspace as eeWorkspaceRouter,
} from "./ee/routes/v1";
import {
signup as v1SignupRouter,
auth as v1AuthRouter,
bot as v1BotRouter,
organization as v1OrganizationRouter,
workspace as v1WorkspaceRouter,
integrationAuth as v1IntegrationAuthRouter,
integration as v1IntegrationRouter,
inviteOrg as v1InviteOrgRouter,
key as v1KeyRouter,
membershipOrg as v1MembershipOrgRouter,
membership as v1MembershipRouter,
key as v1KeyRouter,
inviteOrg as v1InviteOrgRouter,
user as v1UserRouter,
userAction as v1UserActionRouter,
secret as v1SecretRouter,
serviceToken as v1ServiceTokenRouter,
organization as v1OrganizationRouter,
password as v1PasswordRouter,
stripe as v1StripeRouter,
integration as v1IntegrationRouter,
integrationAuth as v1IntegrationAuthRouter,
secret as v1SecretRouter,
secretsFolder as v1SecretsFolder,
serviceToken as v1ServiceTokenRouter,
signup as v1SignupRouter,
stripe as v1StripeRouter,
userAction as v1UserActionRouter,
user as v1UserRouter,
workspace as v1WorkspaceRouter,
} from "./routes/v1";
import {
signup as v2SignupRouter,
@ -95,7 +95,7 @@ const main = async () => {
app.use((req, res, next) => {
// default to IP address provided by Cloudflare
const cfIp = req.headers['cf-connecting-ip'];
const cfIp = req.headers["cf-connecting-ip"];
req.realIP = Array.isArray(cfIp) ? cfIp[0] : (cfIp as string) || req.ip;
next();
});

@ -2,32 +2,32 @@ import { Octokit } from "@octokit/rest";
import { IIntegrationAuth } from "../models";
import { standardRequest } from "../config/request";
import {
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_AWS_PARAMETER_STORE,
INTEGRATION_AWS_SECRET_MANAGER,
INTEGRATION_HEROKU,
INTEGRATION_VERCEL,
INTEGRATION_NETLIFY,
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_CHECKLY,
INTEGRATION_CHECKLY_API_URL,
INTEGRATION_CIRCLECI,
INTEGRATION_CIRCLECI_API_URL,
INTEGRATION_FLYIO,
INTEGRATION_FLYIO_API_URL,
INTEGRATION_GITHUB,
INTEGRATION_GITLAB,
INTEGRATION_RENDER,
INTEGRATION_RAILWAY,
INTEGRATION_FLYIO,
INTEGRATION_CIRCLECI,
INTEGRATION_TRAVISCI,
INTEGRATION_SUPABASE,
INTEGRATION_CHECKLY,
INTEGRATION_HEROKU_API_URL,
INTEGRATION_GITLAB_API_URL,
INTEGRATION_VERCEL_API_URL,
INTEGRATION_HEROKU,
INTEGRATION_HEROKU_API_URL,
INTEGRATION_NETLIFY,
INTEGRATION_NETLIFY_API_URL,
INTEGRATION_RENDER_API_URL,
INTEGRATION_RAILWAY,
INTEGRATION_RAILWAY_API_URL,
INTEGRATION_FLYIO_API_URL,
INTEGRATION_CIRCLECI_API_URL,
INTEGRATION_TRAVISCI_API_URL,
INTEGRATION_RENDER,
INTEGRATION_RENDER_API_URL,
INTEGRATION_SUPABASE,
INTEGRATION_SUPABASE_API_URL,
INTEGRATION_CHECKLY_API_URL
INTEGRATION_TRAVISCI,
INTEGRATION_TRAVISCI_API_URL,
INTEGRATION_VERCEL,
INTEGRATION_VERCEL_API_URL,
} from "../variables";
interface App {
@ -214,7 +214,7 @@ const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => {
const params = new URLSearchParams({
page: String(page),
per_page: String(perPage),
filter: 'all'
filter: "all",
});
const { data } = await standardRequest.get(

@ -1,31 +1,31 @@
import { standardRequest } from "../config/request";
import {
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_HEROKU,
INTEGRATION_VERCEL,
INTEGRATION_NETLIFY,
INTEGRATION_GITHUB,
INTEGRATION_GITLAB,
INTEGRATION_AZURE_TOKEN_URL,
INTEGRATION_HEROKU_TOKEN_URL,
INTEGRATION_VERCEL_TOKEN_URL,
INTEGRATION_NETLIFY_TOKEN_URL,
INTEGRATION_GITHUB,
INTEGRATION_GITHUB_TOKEN_URL,
INTEGRATION_GITLAB,
INTEGRATION_GITLAB_TOKEN_URL,
INTEGRATION_HEROKU,
INTEGRATION_HEROKU_TOKEN_URL,
INTEGRATION_NETLIFY,
INTEGRATION_NETLIFY_TOKEN_URL,
INTEGRATION_VERCEL,
INTEGRATION_VERCEL_TOKEN_URL,
} from "../variables";
import {
getSiteURL,
getClientIdAzure,
getClientSecretAzure,
getClientSecretHeroku,
getClientIdVercel,
getClientSecretVercel,
getClientIdNetlify,
getClientSecretNetlify,
getClientIdGitHub,
getClientSecretGitHub,
getClientIdGitLab,
getClientIdNetlify,
getClientIdVercel,
getClientSecretAzure,
getClientSecretGitHub,
getClientSecretGitLab,
getClientSecretHeroku,
getClientSecretNetlify,
getClientSecretVercel,
getSiteURL,
} from "../config";
interface ExchangeCodeAzureResponse {

@ -1,9 +1,9 @@
import { exchangeCode } from './exchange';
import { exchangeRefresh } from './refresh';
import { getApps } from './apps';
import { getTeams } from './teams';
import { syncSecrets } from './sync';
import { revokeAccess } from './revoke';
import { exchangeCode } from "./exchange";
import { exchangeRefresh } from "./refresh";
import { getApps } from "./apps";
import { getTeams } from "./teams";
import { syncSecrets } from "./sync";
import { revokeAccess } from "./revoke";
export {
exchangeCode,
@ -11,5 +11,5 @@ export {
getApps,
getTeams,
syncSecrets,
revokeAccess
revokeAccess,
}

Some files were not shown because too many files have changed in this diff Show More