Add eslint rule and fix as many issues as possible

This commit is contained in:
Khoa Le
2023-06-19 23:42:04 -04:00
parent e9eacc445d
commit 9d33e4756b
689 changed files with 9446 additions and 21821 deletions
+69 -69
View File
@@ -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,
});
};
+19 -19
View File
@@ -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,
});
};
+17 -17
View File
@@ -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
});
};
+10 -10
View File
@@ -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 = {};
+107 -103
View File
@@ -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"
});
};
+119 -126
View File
@@ -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,
});
};
+31 -28
View File
@@ -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,
});
};
+2 -2
View File
@@ -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.",
});
}
}