mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-13 16:29:28 +00:00
feat(rbac): added new validation to all routes and permission check to most
This commit is contained in:
@@ -17,6 +17,8 @@ import {
|
|||||||
getJwtRefreshSecret
|
getJwtRefreshSecret
|
||||||
} from "../../config";
|
} from "../../config";
|
||||||
import { ActorType } from "../../ee/models";
|
import { ActorType } from "../../ee/models";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/auth";
|
||||||
|
|
||||||
declare module "jsonwebtoken" {
|
declare module "jsonwebtoken" {
|
||||||
export interface UserIDJwtPayload extends jwt.JwtPayload {
|
export interface UserIDJwtPayload extends jwt.JwtPayload {
|
||||||
@@ -32,7 +34,9 @@ declare module "jsonwebtoken" {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const login1 = async (req: Request, res: Response) => {
|
export const login1 = async (req: Request, res: Response) => {
|
||||||
const { email, clientPublicKey }: { email: string; clientPublicKey: string } = req.body;
|
const {
|
||||||
|
body: { email, clientPublicKey }
|
||||||
|
} = await validateRequest(reqValidator.Login1V1, req);
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email
|
email
|
||||||
@@ -76,7 +80,10 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const login2 = async (req: Request, res: Response) => {
|
export const login2 = async (req: Request, res: Response) => {
|
||||||
const { email, clientProof } = req.body;
|
const {
|
||||||
|
body: { email, clientProof }
|
||||||
|
} = await validateRequest(reqValidator.Login2V1, req);
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email
|
email
|
||||||
}).select("+salt +verifier +publicKey +encryptedPrivateKey +iv +tag");
|
}).select("+salt +verifier +publicKey +encryptedPrivateKey +iv +tag");
|
||||||
@@ -194,6 +201,14 @@ export const logout = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCommonPasswords = async (req: Request, res: Response) => {
|
||||||
|
const commonPasswords = fs
|
||||||
|
.readFileSync(path.resolve(__dirname, "../../data/" + "common_passwords.txt"), "utf8")
|
||||||
|
.split("\n");
|
||||||
|
|
||||||
|
return res.status(200).send(commonPasswords);
|
||||||
|
};
|
||||||
|
|
||||||
export const revokeAllSessions = async (req: Request, res: Response) => {
|
export const revokeAllSessions = async (req: Request, res: Response) => {
|
||||||
await TokenVersion.updateMany(
|
await TokenVersion.updateMany(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,10 +2,19 @@ import { Request, Response } from "express";
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { Bot, BotKey } from "../../models";
|
import { Bot, BotKey } from "../../models";
|
||||||
import { createBot } from "../../helpers/bot";
|
import { createBot } from "../../helpers/bot";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/bot";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
import { BadRequestError } from "../../utils/errors";
|
||||||
|
|
||||||
interface BotKey {
|
interface BotKey {
|
||||||
encryptedKey: string;
|
encryptedKey: string;
|
||||||
nonce: string;
|
nonce: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,23 +25,30 @@ interface BotKey {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getBotByWorkspaceId = async (req: Request, res: Response) => {
|
export const getBotByWorkspaceId = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetBotByWorkspaceIdV1, req);
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
let bot = await Bot.findOne({
|
let bot = await Bot.findOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!bot) {
|
if (!bot) {
|
||||||
// case: bot doesn't exist for workspace with id [workspaceId]
|
// case: bot doesn't exist for workspace with id [workspaceId]
|
||||||
// -> create a new bot and return it
|
// -> create a new bot and return it
|
||||||
bot = await createBot({
|
bot = await createBot({
|
||||||
name: "Infisical Bot",
|
name: "Infisical Bot",
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
bot,
|
bot
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,46 +59,69 @@ export const getBotByWorkspaceId = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const setBotActiveState = async (req: Request, res: Response) => {
|
export const setBotActiveState = async (req: Request, res: Response) => {
|
||||||
const { isActive, botKey }: { isActive: boolean, botKey: BotKey } = req.body;
|
const {
|
||||||
|
body: { botKey, isActive },
|
||||||
|
params: { botId }
|
||||||
|
} = await validateRequest(reqValidator.SetBotActiveStateV1, req);
|
||||||
|
|
||||||
if (isActive) {
|
const bot = await Bot.findById(botId);
|
||||||
// bot state set to active -> share workspace key with bot
|
if (!bot) {
|
||||||
if (!botKey?.encryptedKey || !botKey?.nonce) {
|
throw BadRequestError({ message: "Bot not found" });
|
||||||
return res.status(400).send({
|
}
|
||||||
message: "Failed to set bot state to active - missing bot key",
|
const userId = req.user._id;
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await BotKey.findOneAndUpdate({
|
const { permission } = await getUserProjectPermissions(userId, bot.workspace.toString());
|
||||||
workspace: req.bot.workspace,
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
}, {
|
ProjectPermissionActions.Edit,
|
||||||
encryptedKey: botKey.encryptedKey,
|
ProjectPermissionSub.Integrations
|
||||||
nonce: botKey.nonce,
|
);
|
||||||
sender: req.user._id,
|
|
||||||
bot: req.bot._id,
|
if (isActive) {
|
||||||
workspace: req.bot.workspace,
|
// bot state set to active -> share workspace key with bot
|
||||||
}, {
|
if (!botKey?.encryptedKey || !botKey?.nonce) {
|
||||||
upsert: true,
|
return res.status(400).send({
|
||||||
new: true,
|
message: "Failed to set bot state to active - missing bot key"
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// case: bot state set to inactive -> delete bot's workspace key
|
|
||||||
await BotKey.deleteOne({
|
|
||||||
bot: req.bot._id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bot = await Bot.findOneAndUpdate({
|
await BotKey.findOneAndUpdate(
|
||||||
_id: req.bot._id,
|
{
|
||||||
}, {
|
workspace: bot.workspace
|
||||||
isActive,
|
},
|
||||||
}, {
|
{
|
||||||
new: true,
|
encryptedKey: botKey.encryptedKey,
|
||||||
|
nonce: botKey.nonce,
|
||||||
|
sender: userId,
|
||||||
|
bot: bot._id,
|
||||||
|
workspace: bot.workspace
|
||||||
|
},
|
||||||
|
{
|
||||||
|
upsert: true,
|
||||||
|
new: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// case: bot state set to inactive -> delete bot's workspace key
|
||||||
|
await BotKey.deleteOne({
|
||||||
|
bot: bot._id
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!bot) throw new Error("Failed to update bot active state");
|
const updatedBot = await Bot.findOneAndUpdate(
|
||||||
|
{
|
||||||
|
_id: bot._id
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isActive
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
if (!updatedBot) throw new Error("Failed to update bot active state");
|
||||||
bot,
|
|
||||||
});
|
return res.status(200).send({
|
||||||
|
bot
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Request, Response } from "express";
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { standardRequest } from "../../config/request";
|
import { standardRequest } from "../../config/request";
|
||||||
import { getApps, getTeams, revokeAccess } from "../../integrations";
|
import { getApps, getTeams, revokeAccess } from "../../integrations";
|
||||||
import { Bot, IntegrationAuth } from "../../models";
|
import { Bot, IntegrationAuth, Workspace } from "../../models";
|
||||||
import { EventType } from "../../ee/models";
|
import { EventType } from "../../ee/models";
|
||||||
import { IntegrationService } from "../../services";
|
import { IntegrationService } from "../../services";
|
||||||
import { EEAuditLogService } from "../../ee/services";
|
import { EEAuditLogService } from "../../ee/services";
|
||||||
@@ -18,12 +18,25 @@ import {
|
|||||||
getIntegrationOptions as getIntegrationOptionsFunc
|
getIntegrationOptions as getIntegrationOptionsFunc
|
||||||
} from "../../variables";
|
} from "../../variables";
|
||||||
import { exchangeRefresh } from "../../integrations";
|
import { exchangeRefresh } from "../../integrations";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/integrationAuth";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
import { getIntegrationAuthAccessHelper } from "../../helpers";
|
||||||
|
import { ObjectId } from "mongodb";
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Return integration authorization with id [integrationAuthId]
|
* Return integration authorization with id [integrationAuthId]
|
||||||
*/
|
*/
|
||||||
export const getIntegrationAuth = async (req: Request, res: Response) => {
|
export const getIntegrationAuth = async (req: Request, res: Response) => {
|
||||||
const { integrationAuthId } = req.params;
|
const {
|
||||||
|
params: { integrationAuthId }
|
||||||
|
} = await validateRequest(reqValidator.GetIntegrationAuthV1, req);
|
||||||
|
|
||||||
const integrationAuth = await IntegrationAuth.findById(integrationAuthId);
|
const integrationAuth = await IntegrationAuth.findById(integrationAuthId);
|
||||||
|
|
||||||
if (!integrationAuth)
|
if (!integrationAuth)
|
||||||
@@ -31,6 +44,15 @@ export const getIntegrationAuth = async (req: Request, res: Response) => {
|
|||||||
message: "Failed to find integration authorization"
|
message: "Failed to find integration authorization"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
integrationAuth.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
integrationAuth
|
integrationAuth
|
||||||
});
|
});
|
||||||
@@ -52,14 +74,18 @@ export const getIntegrationOptions = async (req: Request, res: Response) => {
|
|||||||
*/
|
*/
|
||||||
export const oAuthExchange = async (req: Request, res: Response) => {
|
export const oAuthExchange = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
workspaceId,
|
body: { integration, workspaceId, code, url }
|
||||||
code,
|
} = await validateRequest(reqValidator.OauthExchangeV1, req);
|
||||||
integration,
|
|
||||||
url
|
|
||||||
} = 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 || [];
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
|
const workspace = await Workspace.findById(workspaceId);
|
||||||
|
const environments = workspace?.environments || [];
|
||||||
if (environments.length === 0) {
|
if (environments.length === 0) {
|
||||||
throw new Error("Failed to get environments");
|
throw new Error("Failed to get environments");
|
||||||
}
|
}
|
||||||
@@ -99,25 +125,16 @@ export const oAuthExchange = async (req: Request, res: Response) => {
|
|||||||
export const saveIntegrationToken = async (req: Request, res: Response) => {
|
export const saveIntegrationToken = async (req: Request, res: Response) => {
|
||||||
// TODO: refactor
|
// TODO: refactor
|
||||||
// TODO: check if access token is valid for each integration
|
// TODO: check if access token is valid for each integration
|
||||||
|
|
||||||
let integrationAuth;
|
let integrationAuth;
|
||||||
const {
|
const {
|
||||||
workspaceId,
|
body: { workspaceId, integration, url, accessId, namespace, accessToken, refreshToken }
|
||||||
accessId,
|
} = await validateRequest(reqValidator.SaveIntegrationAccessTokenV1, req);
|
||||||
refreshToken,
|
|
||||||
accessToken,
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
url,
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
namespace,
|
ProjectPermissionActions.Create,
|
||||||
integration
|
ProjectPermissionSub.Integrations
|
||||||
}: {
|
);
|
||||||
workspaceId: string;
|
|
||||||
accessId: string | undefined;
|
|
||||||
refreshToken: string | undefined;
|
|
||||||
accessToken: string | undefined;
|
|
||||||
url: string;
|
|
||||||
namespace: string;
|
|
||||||
integration: string;
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
const bot = await Bot.findOne({
|
const bot = await Bot.findOne({
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
@@ -195,13 +212,29 @@ export const saveIntegrationToken = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getIntegrationAuthApps = async (req: Request, res: Response) => {
|
export const getIntegrationAuthApps = async (req: Request, res: Response) => {
|
||||||
const teamId = req.query.teamId as string;
|
const {
|
||||||
const workspaceSlug = req.query.workspaceSlug as string;
|
params: { integrationAuthId },
|
||||||
|
query: { teamId, workspaceSlug }
|
||||||
|
} = await validateRequest(reqValidator.GetIntegrationAuthAppsV1, req);
|
||||||
|
|
||||||
|
// TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions
|
||||||
|
const { integrationAuth, accessToken, accessId } = await getIntegrationAuthAccessHelper({
|
||||||
|
integrationAuthId: new ObjectId(integrationAuthId)
|
||||||
|
});
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
integrationAuth.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
const apps = await getApps({
|
const apps = await getApps({
|
||||||
integrationAuth: req.integrationAuth,
|
integrationAuth: integrationAuth,
|
||||||
accessToken: req.accessToken,
|
accessToken: accessToken,
|
||||||
accessId: req.accessId,
|
accessId: accessId,
|
||||||
...(teamId && { teamId }),
|
...(teamId && { teamId }),
|
||||||
...(workspaceSlug && { workspaceSlug })
|
...(workspaceSlug && { workspaceSlug })
|
||||||
});
|
});
|
||||||
@@ -218,9 +251,27 @@ export const getIntegrationAuthApps = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getIntegrationAuthTeams = async (req: Request, res: Response) => {
|
export const getIntegrationAuthTeams = async (req: Request, res: Response) => {
|
||||||
|
const {
|
||||||
|
params: { integrationAuthId }
|
||||||
|
} = await validateRequest(reqValidator.GetIntegrationAuthTeamsV1, req);
|
||||||
|
|
||||||
|
// TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions
|
||||||
|
const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({
|
||||||
|
integrationAuthId: new ObjectId(integrationAuthId)
|
||||||
|
});
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
integrationAuth.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
const teams = await getTeams({
|
const teams = await getTeams({
|
||||||
integrationAuth: req.integrationAuth,
|
integrationAuth: integrationAuth,
|
||||||
accessToken: req.accessToken
|
accessToken: accessToken
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
@@ -235,7 +286,24 @@ export const getIntegrationAuthTeams = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getIntegrationAuthVercelBranches = async (req: Request, res: Response) => {
|
export const getIntegrationAuthVercelBranches = async (req: Request, res: Response) => {
|
||||||
const appId = req.query.appId as string;
|
const {
|
||||||
|
params: { integrationAuthId },
|
||||||
|
query: { appId }
|
||||||
|
} = await validateRequest(reqValidator.GetIntegrationAuthVercelBranchesV1, req);
|
||||||
|
|
||||||
|
// TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions
|
||||||
|
const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({
|
||||||
|
integrationAuthId: new ObjectId(integrationAuthId)
|
||||||
|
});
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
integrationAuth.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
interface VercelBranch {
|
interface VercelBranch {
|
||||||
ref: string;
|
ref: string;
|
||||||
@@ -245,9 +313,9 @@ export const getIntegrationAuthVercelBranches = async (req: Request, res: Respon
|
|||||||
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
projectId: appId,
|
projectId: appId,
|
||||||
...(req.integrationAuth.teamId
|
...(integrationAuth.teamId
|
||||||
? {
|
? {
|
||||||
teamId: req.integrationAuth.teamId
|
teamId: integrationAuth.teamId
|
||||||
}
|
}
|
||||||
: {})
|
: {})
|
||||||
});
|
});
|
||||||
@@ -260,7 +328,7 @@ export const getIntegrationAuthVercelBranches = async (req: Request, res: Respon
|
|||||||
{
|
{
|
||||||
params,
|
params,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${req.accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
"Accept-Encoding": "application/json"
|
"Accept-Encoding": "application/json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -281,7 +349,24 @@ export const getIntegrationAuthVercelBranches = async (req: Request, res: Respon
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: Response) => {
|
export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: Response) => {
|
||||||
const appId = req.query.appId as string;
|
const {
|
||||||
|
params: { integrationAuthId },
|
||||||
|
query: { appId }
|
||||||
|
} = await validateRequest(reqValidator.GetIntegrationAuthRailwayEnvironmentsV1, req);
|
||||||
|
|
||||||
|
// TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions
|
||||||
|
const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({
|
||||||
|
integrationAuthId: new ObjectId(integrationAuthId)
|
||||||
|
});
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
integrationAuth.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
interface RailwayEnvironment {
|
interface RailwayEnvironment {
|
||||||
node: {
|
node: {
|
||||||
@@ -331,7 +416,7 @@ export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: R
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${req.accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,7 +442,24 @@ export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: R
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getIntegrationAuthRailwayServices = async (req: Request, res: Response) => {
|
export const getIntegrationAuthRailwayServices = async (req: Request, res: Response) => {
|
||||||
const appId = req.query.appId as string;
|
const {
|
||||||
|
params: { integrationAuthId },
|
||||||
|
query: { appId }
|
||||||
|
} = await validateRequest(reqValidator.GetIntegrationAuthRailwayServicesV1, req);
|
||||||
|
|
||||||
|
// TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions
|
||||||
|
const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({
|
||||||
|
integrationAuthId: new ObjectId(integrationAuthId)
|
||||||
|
});
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
integrationAuth.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
interface RailwayService {
|
interface RailwayService {
|
||||||
node: {
|
node: {
|
||||||
@@ -422,7 +524,7 @@ export const getIntegrationAuthRailwayServices = async (req: Request, res: Respo
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${req.accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -446,7 +548,6 @@ export const getIntegrationAuthRailwayServices = async (req: Request, res: Respo
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getIntegrationAuthBitBucketWorkspaces = async (req: Request, res: Response) => {
|
export const getIntegrationAuthBitBucketWorkspaces = async (req: Request, res: Response) => {
|
||||||
|
|
||||||
interface WorkspaceResponse {
|
interface WorkspaceResponse {
|
||||||
size: number;
|
size: number;
|
||||||
page: number;
|
page: number;
|
||||||
@@ -466,31 +567,46 @@ export const getIntegrationAuthBitBucketWorkspaces = async (req: Request, res: R
|
|||||||
updated_on: string;
|
updated_on: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
params: { integrationAuthId }
|
||||||
|
} = await validateRequest(reqValidator.GetIntegrationAuthBitbucketWorkspacesV1, req);
|
||||||
|
|
||||||
|
// TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions
|
||||||
|
const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({
|
||||||
|
integrationAuthId: new ObjectId(integrationAuthId)
|
||||||
|
});
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
integrationAuth.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
const workspaces: Workspace[] = [];
|
const workspaces: Workspace[] = [];
|
||||||
let hasNextPage = true;
|
let hasNextPage = true;
|
||||||
let workspaceUrl = `${INTEGRATION_BITBUCKET_API_URL}/2.0/workspaces`
|
let workspaceUrl = `${INTEGRATION_BITBUCKET_API_URL}/2.0/workspaces`;
|
||||||
|
|
||||||
while (hasNextPage) {
|
while (hasNextPage) {
|
||||||
const { data }: { data: WorkspaceResponse } = await standardRequest.get(
|
const { data }: { data: WorkspaceResponse } = await standardRequest.get(workspaceUrl, {
|
||||||
workspaceUrl,
|
headers: {
|
||||||
{
|
Authorization: `Bearer ${accessToken}`,
|
||||||
headers: {
|
"Accept-Encoding": "application/json"
|
||||||
Authorization: `Bearer ${req.accessToken}`,
|
|
||||||
"Accept-Encoding": "application/json"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
if (data?.values.length > 0) {
|
if (data?.values.length > 0) {
|
||||||
data.values.forEach((workspace) => {
|
data.values.forEach((workspace) => {
|
||||||
workspaces.push(workspace)
|
workspaces.push(workspace);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.next) {
|
if (data.next) {
|
||||||
workspaceUrl = data.next
|
workspaceUrl = data.next;
|
||||||
} else {
|
} else {
|
||||||
hasNextPage = false
|
hasNextPage = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,7 +622,24 @@ export const getIntegrationAuthBitBucketWorkspaces = async (req: Request, res: R
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getIntegrationAuthNorthflankSecretGroups = async (req: Request, res: Response) => {
|
export const getIntegrationAuthNorthflankSecretGroups = async (req: Request, res: Response) => {
|
||||||
const appId = req.query.appId as string;
|
const {
|
||||||
|
params: { integrationAuthId },
|
||||||
|
query: { appId }
|
||||||
|
} = await validateRequest(reqValidator.GetIntegrationAuthNorthflankSecretGroupsV1, req);
|
||||||
|
|
||||||
|
// TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions
|
||||||
|
const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({
|
||||||
|
integrationAuthId: new ObjectId(integrationAuthId)
|
||||||
|
});
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
integrationAuth.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
interface NorthflankSecretGroup {
|
interface NorthflankSecretGroup {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -528,27 +661,25 @@ export const getIntegrationAuthNorthflankSecretGroups = async (req: Request, res
|
|||||||
const perPage = 10;
|
const perPage = 10;
|
||||||
let hasMorePages = true;
|
let hasMorePages = true;
|
||||||
|
|
||||||
while(hasMorePages) {
|
while (hasMorePages) {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
page: String(page),
|
page: String(page),
|
||||||
per_page: String(perPage),
|
per_page: String(perPage),
|
||||||
filter: "all",
|
filter: "all"
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
data: {
|
data: { secrets }
|
||||||
secrets
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} = await standardRequest.get<{ data: { secrets: NorthflankSecretGroup[] }}>(
|
} = await standardRequest.get<{ data: { secrets: NorthflankSecretGroup[] } }>(
|
||||||
`${INTEGRATION_NORTHFLANK_API_URL}/v1/projects/${appId}/secrets`,
|
`${INTEGRATION_NORTHFLANK_API_URL}/v1/projects/${appId}/secrets`,
|
||||||
{
|
{
|
||||||
params,
|
params,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${req.accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
"Accept-Encoding": "application/json",
|
"Accept-Encoding": "application/json"
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -570,7 +701,7 @@ export const getIntegrationAuthNorthflankSecretGroups = async (req: Request, res
|
|||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secretGroups
|
secretGroups
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return list of build configs for TeamCity project with id [appId]
|
* Return list of build configs for TeamCity project with id [appId]
|
||||||
@@ -579,7 +710,23 @@ export const getIntegrationAuthNorthflankSecretGroups = async (req: Request, res
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getIntegrationAuthTeamCityBuildConfigs = async (req: Request, res: Response) => {
|
export const getIntegrationAuthTeamCityBuildConfigs = async (req: Request, res: Response) => {
|
||||||
const appId = req.query.appId as string;
|
const {
|
||||||
|
params: { integrationAuthId,appId },
|
||||||
|
} = await validateRequest(reqValidator.GetIntegrationAuthTeamCityBuildConfigsV1 , req);
|
||||||
|
|
||||||
|
// TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions
|
||||||
|
const { integrationAuth } = await getIntegrationAuthAccessHelper({
|
||||||
|
integrationAuthId: new ObjectId(integrationAuthId)
|
||||||
|
});
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
integrationAuth.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
interface TeamCityBuildConfig {
|
interface TeamCityBuildConfig {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -630,30 +777,48 @@ export const getIntegrationAuthTeamCityBuildConfigs = async (req: Request, res:
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const deleteIntegrationAuth = async (req: Request, res: Response) => {
|
export const deleteIntegrationAuth = async (req: Request, res: Response) => {
|
||||||
const integrationAuth = await revokeAccess({
|
const {
|
||||||
integrationAuth: req.integrationAuth,
|
params: { integrationAuthId }
|
||||||
accessToken: req.accessToken
|
} = await validateRequest(reqValidator.DeleteIntegrationAuthV1, req);
|
||||||
|
|
||||||
|
// TODO(akhilmhdh): remove class -> static function path and makes these into reusable independent functions
|
||||||
|
const { integrationAuth, accessToken } = await getIntegrationAuthAccessHelper({
|
||||||
|
integrationAuthId: new ObjectId(integrationAuthId)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!integrationAuth) return res.status(400).send({
|
const { permission } = await getUserProjectPermissions(
|
||||||
message: "Failed to find integration authorization"
|
req.user._id,
|
||||||
|
integrationAuth.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
|
const deletedIntegrationAuth = await revokeAccess({
|
||||||
|
integrationAuth: integrationAuth,
|
||||||
|
accessToken: accessToken
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!deletedIntegrationAuth)
|
||||||
|
return res.status(400).send({
|
||||||
|
message: "Failed to find integration authorization"
|
||||||
|
});
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
req.authData,
|
req.authData,
|
||||||
{
|
{
|
||||||
type: EventType.UNAUTHORIZE_INTEGRATION,
|
type: EventType.UNAUTHORIZE_INTEGRATION,
|
||||||
metadata: {
|
metadata: {
|
||||||
integration: integrationAuth.integration
|
integration: deletedIntegrationAuth.integration
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
workspaceId: integrationAuth.workspace
|
workspaceId: deletedIntegrationAuth.workspace
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
integrationAuth
|
integrationAuth: deletedIntegrationAuth
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { Folder, Integration } from "../../models";
|
import { IWorkspace, Integration, Folder, IntegrationAuth } from "../../models";
|
||||||
import { EventService } from "../../services";
|
import { EventService } from "../../services";
|
||||||
import { eventStartIntegration } from "../../events";
|
import { eventStartIntegration } from "../../events";
|
||||||
import { getFolderByPath } from "../../services/FolderService";
|
import { getFolderByPath } from "../../services/FolderService";
|
||||||
@@ -8,6 +8,14 @@ import { BadRequestError } from "../../utils/errors";
|
|||||||
import { EEAuditLogService } from "../../ee/services";
|
import { EEAuditLogService } from "../../ee/services";
|
||||||
import { EventType } from "../../ee/models";
|
import { EventType } from "../../ee/models";
|
||||||
import { syncSecretsToActiveIntegrationsQueue } from "../../queues/integrations/syncSecretsToThirdPartyServices";
|
import { syncSecretsToActiveIntegrationsQueue } from "../../queues/integrations/syncSecretsToThirdPartyServices";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/integration";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create/initialize an (empty) integration for integration authorization
|
* Create/initialize an (empty) integration for integration authorization
|
||||||
@@ -17,24 +25,42 @@ import { syncSecretsToActiveIntegrationsQueue } from "../../queues/integrations/
|
|||||||
*/
|
*/
|
||||||
export const createIntegration = async (req: Request, res: Response) => {
|
export const createIntegration = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
integrationAuthId,
|
body: {
|
||||||
app,
|
isActive,
|
||||||
appId,
|
sourceEnvironment,
|
||||||
isActive,
|
secretPath,
|
||||||
sourceEnvironment,
|
app,
|
||||||
targetEnvironment,
|
path,
|
||||||
targetEnvironmentId,
|
appId,
|
||||||
targetService,
|
owner,
|
||||||
targetServiceId,
|
region,
|
||||||
owner,
|
targetService,
|
||||||
path,
|
targetServiceId,
|
||||||
region,
|
integrationAuthId,
|
||||||
secretPath,
|
targetEnvironment,
|
||||||
metadata
|
targetEnvironmentId
|
||||||
} = req.body;
|
}
|
||||||
|
} = await validateRequest(reqValidator.CreateIntegrationV1, req);
|
||||||
|
|
||||||
|
const integrationAuth = await IntegrationAuth.findById(integrationAuthId)
|
||||||
|
.populate<{ workspace: IWorkspace }>("workspace")
|
||||||
|
.select(
|
||||||
|
"+refreshCiphertext +refreshIV +refreshTag +accessCiphertext +accessIV +accessTag +accessExpiresAt"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!integrationAuth) throw BadRequestError({ message: "Integration auth not found" });
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
integrationAuth.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
const folders = await Folder.findOne({
|
const folders = await Folder.findOne({
|
||||||
workspace: req.integrationAuth.workspace._id,
|
workspace: integrationAuth.workspace._id,
|
||||||
environment: sourceEnvironment
|
environment: sourceEnvironment
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -42,7 +68,7 @@ export const createIntegration = async (req: Request, res: Response) => {
|
|||||||
const folder = getFolderByPath(folders.nodes, secretPath);
|
const folder = getFolderByPath(folders.nodes, secretPath);
|
||||||
if (!folder) {
|
if (!folder) {
|
||||||
throw BadRequestError({
|
throw BadRequestError({
|
||||||
message: "Path for service token does not exist"
|
message: "Folder path doesn't exist"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,7 +77,7 @@ export const createIntegration = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
// initialize new integration after saving integration access token
|
// initialize new integration after saving integration access token
|
||||||
const integration = await new Integration({
|
const integration = await new Integration({
|
||||||
workspace: req.integrationAuth.workspace._id,
|
workspace: integrationAuth.workspace._id,
|
||||||
environment: sourceEnvironment,
|
environment: sourceEnvironment,
|
||||||
isActive,
|
isActive,
|
||||||
app,
|
app,
|
||||||
@@ -64,9 +90,8 @@ export const createIntegration = async (req: Request, res: Response) => {
|
|||||||
path,
|
path,
|
||||||
region,
|
region,
|
||||||
secretPath,
|
secretPath,
|
||||||
integration: req.integrationAuth.integration,
|
integration: integrationAuth.integration,
|
||||||
integrationAuth: new Types.ObjectId(integrationAuthId),
|
integrationAuth: new Types.ObjectId(integrationAuthId)
|
||||||
metadata
|
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
if (integration) {
|
if (integration) {
|
||||||
@@ -120,17 +145,32 @@ export const updateIntegration = async (req: Request, res: Response) => {
|
|||||||
// integration has the correct fields populated in [Integration]
|
// integration has the correct fields populated in [Integration]
|
||||||
|
|
||||||
const {
|
const {
|
||||||
environment,
|
body: {
|
||||||
isActive,
|
environment,
|
||||||
app,
|
isActive,
|
||||||
appId,
|
app,
|
||||||
targetEnvironment,
|
appId,
|
||||||
owner, // github-specific integration param
|
targetEnvironment,
|
||||||
secretPath
|
owner, // github-specific integration param
|
||||||
} = req.body;
|
secretPath
|
||||||
|
},
|
||||||
|
params: { integrationId }
|
||||||
|
} = await validateRequest(reqValidator.UpdateIntegrationV1, req);
|
||||||
|
|
||||||
|
const integration = await Integration.findById(integrationId);
|
||||||
|
if (!integration) throw BadRequestError({ message: "Integration not found" });
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
integration.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
const folders = await Folder.findOne({
|
const folders = await Folder.findOne({
|
||||||
workspace: req.integration.workspace,
|
workspace: integration.workspace,
|
||||||
environment
|
environment
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -143,9 +183,9 @@ export const updateIntegration = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const integration = await Integration.findOneAndUpdate(
|
const updatedIntegration = await Integration.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
_id: req.integration._id
|
_id: integration._id
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
environment,
|
environment,
|
||||||
@@ -161,18 +201,18 @@ export const updateIntegration = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (integration) {
|
if (updatedIntegration) {
|
||||||
// trigger event - push secrets
|
// trigger event - push secrets
|
||||||
EventService.handleEvent({
|
EventService.handleEvent({
|
||||||
event: eventStartIntegration({
|
event: eventStartIntegration({
|
||||||
workspaceId: integration.workspace,
|
workspaceId: updatedIntegration.workspace,
|
||||||
environment
|
environment
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
integration
|
integration: updatedIntegration
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -183,13 +223,27 @@ export const updateIntegration = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const deleteIntegration = async (req: Request, res: Response) => {
|
export const deleteIntegration = async (req: Request, res: Response) => {
|
||||||
const { integrationId } = req.params;
|
const {
|
||||||
|
params: { integrationId }
|
||||||
|
} = await validateRequest(reqValidator.DeleteIntegrationV1, req);
|
||||||
|
|
||||||
const integration = await Integration.findOneAndDelete({
|
const integration = await Integration.findById(integrationId);
|
||||||
|
if (!integration) throw BadRequestError({ message: "Integration not found" });
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
integration.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
|
const deletedIntegration = await Integration.findOneAndDelete({
|
||||||
_id: integrationId
|
_id: integrationId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!integration) throw new Error("Failed to find integration");
|
if (!deletedIntegration) throw new Error("Failed to find integration");
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
req.authData,
|
req.authData,
|
||||||
|
|||||||
@@ -4,6 +4,14 @@ import { Key } from "../../models";
|
|||||||
import { findMembership } from "../../helpers/membership";
|
import { findMembership } from "../../helpers/membership";
|
||||||
import { EventType } from "../../ee/models";
|
import { EventType } from "../../ee/models";
|
||||||
import { EEAuditLogService } from "../../ee/services";
|
import { EEAuditLogService } from "../../ee/services";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/key";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add (encrypted) copy of workspace key for workspace with id [workspaceId] for user with
|
* Add (encrypted) copy of workspace key for workspace with id [workspaceId] for user with
|
||||||
@@ -13,13 +21,21 @@ import { EEAuditLogService } from "../../ee/services";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const uploadKey = async (req: Request, res: Response) => {
|
export const uploadKey = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
const { key } = req.body;
|
params: { workspaceId },
|
||||||
|
body: { key }
|
||||||
|
} = await validateRequest(reqValidator.UploadKeyV1, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionSub.Member
|
||||||
|
);
|
||||||
|
|
||||||
// validate membership of receiver
|
// validate membership of receiver
|
||||||
const receiverMembership = await findMembership({
|
const receiverMembership = await findMembership({
|
||||||
user: key.userId,
|
user: key.userId,
|
||||||
workspace: workspaceId,
|
workspace: workspaceId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!receiverMembership) {
|
if (!receiverMembership) {
|
||||||
@@ -31,12 +47,12 @@ export const uploadKey = async (req: Request, res: Response) => {
|
|||||||
nonce: key.nonce,
|
nonce: key.nonce,
|
||||||
sender: req.user._id,
|
sender: req.user._id,
|
||||||
receiver: key.userId,
|
receiver: key.userId,
|
||||||
workspace: workspaceId,
|
workspace: workspaceId
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: "Successfully uploaded key to workspace",
|
message: "Successfully uploaded key to workspace"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,21 +62,23 @@ export const uploadKey = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getLatestKey = async (req: Request, res: Response) => {
|
export const getLatestKey = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetLatestKeyV1, req);
|
||||||
|
|
||||||
// get latest key
|
// get latest key
|
||||||
const latestKey = await Key.find({
|
const latestKey = await Key.find({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
receiver: req.user._id,
|
receiver: req.user._id
|
||||||
})
|
})
|
||||||
.sort({ createdAt: -1 })
|
.sort({ createdAt: -1 })
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.populate("sender", "+publicKey");
|
.populate("sender", "+publicKey");
|
||||||
|
|
||||||
const resObj: any = {};
|
const resObj: any = {};
|
||||||
|
|
||||||
if (latestKey.length > 0) {
|
if (latestKey.length > 0) {
|
||||||
resObj["latestKey"] = latestKey[0];
|
resObj["latestKey"] = latestKey[0];
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
req.authData,
|
req.authData,
|
||||||
{
|
{
|
||||||
@@ -73,7 +91,7 @@ export const getLatestKey = async (req: Request, res: Response) => {
|
|||||||
workspaceId: new Types.ObjectId(workspaceId)
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send(resObj);
|
return res.status(200).send(resObj);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,14 @@ import { sendMail } from "../../helpers/nodemailer";
|
|||||||
import { ACCEPTED, ADMIN, MEMBER } from "../../variables";
|
import { ACCEPTED, ADMIN, MEMBER } from "../../variables";
|
||||||
import { getSiteURL } from "../../config";
|
import { getSiteURL } from "../../config";
|
||||||
import { EEAuditLogService } from "../../ee/services";
|
import { EEAuditLogService } from "../../ee/services";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/membership";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that user is a member of workspace with id [workspaceId]
|
* Check that user is a member of workspace with id [workspaceId]
|
||||||
@@ -15,7 +23,10 @@ import { EEAuditLogService } from "../../ee/services";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const validateMembership = async (req: Request, res: Response) => {
|
export const validateMembership = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.ValidateMembershipV1, req);
|
||||||
|
|
||||||
// validate membership
|
// validate membership
|
||||||
const membership = await findMembership({
|
const membership = await findMembership({
|
||||||
user: req.user._id,
|
user: req.user._id,
|
||||||
@@ -38,7 +49,9 @@ export const validateMembership = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const deleteMembership = async (req: Request, res: Response) => {
|
export const deleteMembership = async (req: Request, res: Response) => {
|
||||||
const { membershipId } = req.params;
|
const {
|
||||||
|
params: { membershipId }
|
||||||
|
} = await validateRequest(reqValidator.DeleteMembershipV1, req);
|
||||||
|
|
||||||
// check if membership to delete exists
|
// check if membership to delete exists
|
||||||
const membershipToDelete = await Membership.findOne({
|
const membershipToDelete = await Membership.findOne({
|
||||||
@@ -49,21 +62,14 @@ export const deleteMembership = async (req: Request, res: Response) => {
|
|||||||
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
|
const { permission } = await getUserProjectPermissions(
|
||||||
// whose membership we wish to delete
|
req.user._id,
|
||||||
const membership = await Membership.findOne({
|
membershipToDelete.workspace.toString()
|
||||||
user: req.user._id,
|
);
|
||||||
workspace: membershipToDelete.workspace
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
});
|
ProjectPermissionActions.Delete,
|
||||||
|
ProjectPermissionSub.Member
|
||||||
if (!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");
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete workspace membership
|
// delete workspace membership
|
||||||
const deletedMembership = await deleteMember({
|
const deletedMembership = await deleteMember({
|
||||||
@@ -80,7 +86,7 @@ export const deleteMembership = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
workspaceId: membership.workspace
|
workspaceId: membershipToDelete.workspace
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -96,40 +102,34 @@ export const deleteMembership = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const changeMembershipRole = async (req: Request, res: Response) => {
|
export const changeMembershipRole = async (req: Request, res: Response) => {
|
||||||
const { membershipId } = req.params;
|
const {
|
||||||
const { role } = req.body;
|
body: { role },
|
||||||
|
params: { membershipId }
|
||||||
|
} = await validateRequest(reqValidator.ChangeMembershipRoleV1, req);
|
||||||
|
|
||||||
if (![ADMIN, MEMBER].includes(role)) {
|
if (![ADMIN, MEMBER].includes(role)) {
|
||||||
throw new Error("Failed to validate role");
|
throw new Error("Failed to validate role");
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate target membership
|
// validate target membership
|
||||||
const membershipToChangeRole = await Membership
|
const membershipToChangeRole = await Membership.findById(membershipId).populate<{ user: IUser }>(
|
||||||
.findById(membershipId)
|
"user"
|
||||||
.populate<{ user: IUser }>("user");
|
);
|
||||||
|
|
||||||
if (!membershipToChangeRole) {
|
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
|
const { permission } = await getUserProjectPermissions(
|
||||||
// workspace
|
req.user._id,
|
||||||
const membership = await findMembership({
|
membershipToChangeRole.workspace.toString()
|
||||||
user: req.user._id,
|
);
|
||||||
workspace: membershipToChangeRole.workspace
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
});
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionSub.Member
|
||||||
if (!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");
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldRole = membershipToChangeRole.role;
|
const oldRole = membershipToChangeRole.role;
|
||||||
|
|
||||||
membershipToChangeRole.role = role;
|
membershipToChangeRole.role = role;
|
||||||
await membershipToChangeRole.save();
|
await membershipToChangeRole.save();
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import {
|
|||||||
getSiteURL
|
getSiteURL
|
||||||
} from "../../config";
|
} from "../../config";
|
||||||
import { ActorType } from "../../ee/models";
|
import { ActorType } from "../../ee/models";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/auth";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Password reset step 1: Send email verification link to email [email]
|
* Password reset step 1: Send email verification link to email [email]
|
||||||
@@ -23,7 +25,9 @@ import { ActorType } from "../../ee/models";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const emailPasswordReset = async (req: Request, res: Response) => {
|
export const emailPasswordReset = async (req: Request, res: Response) => {
|
||||||
const email: string = req.body.email;
|
const {
|
||||||
|
body: { email }
|
||||||
|
} = await validateRequest(reqValidator.EmailPasswordResetV1, req);
|
||||||
|
|
||||||
const user = await User.findOne({ email }).select("+publicKey");
|
const user = await User.findOne({ email }).select("+publicKey");
|
||||||
if (!user || !user?.publicKey) {
|
if (!user || !user?.publicKey) {
|
||||||
@@ -62,7 +66,9 @@ export const emailPasswordReset = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const emailPasswordResetVerify = async (req: Request, res: Response) => {
|
export const emailPasswordResetVerify = async (req: Request, res: Response) => {
|
||||||
const { email, code } = req.body;
|
const {
|
||||||
|
body: { email, code }
|
||||||
|
} = await validateRequest(reqValidator.EmailPasswordResetVerifyV1, req);
|
||||||
|
|
||||||
const user = await User.findOne({ email }).select("+publicKey");
|
const user = await User.findOne({ email }).select("+publicKey");
|
||||||
if (!user || !user?.publicKey) {
|
if (!user || !user?.publicKey) {
|
||||||
@@ -103,8 +109,10 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => {
|
|||||||
*/
|
*/
|
||||||
export const srp1 = async (req: Request, res: Response) => {
|
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 {
|
||||||
|
body: { clientPublicKey }
|
||||||
|
} = await validateRequest(reqValidator.Srp1V1, req);
|
||||||
|
|
||||||
const { clientPublicKey } = req.body;
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email: req.user.email
|
email: req.user.email
|
||||||
}).select("+salt +verifier");
|
}).select("+salt +verifier");
|
||||||
@@ -149,16 +157,18 @@ export const srp1 = async (req: Request, res: Response) => {
|
|||||||
*/
|
*/
|
||||||
export const changePassword = async (req: Request, res: Response) => {
|
export const changePassword = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
clientProof,
|
body: {
|
||||||
protectedKey,
|
clientProof,
|
||||||
protectedKeyIV,
|
protectedKey,
|
||||||
protectedKeyTag,
|
protectedKeyIV,
|
||||||
encryptedPrivateKey,
|
protectedKeyTag,
|
||||||
encryptedPrivateKeyIV,
|
encryptedPrivateKey,
|
||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyIV,
|
||||||
salt,
|
encryptedPrivateKeyTag,
|
||||||
verifier
|
salt,
|
||||||
} = req.body;
|
verifier
|
||||||
|
}
|
||||||
|
} = await validateRequest(reqValidator.ChangePasswordV1, req);
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email: req.user.email
|
email: req.user.email
|
||||||
@@ -208,10 +218,7 @@ export const changePassword = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (req.authData.actor.type === ActorType.USER && req.authData.tokenVersionId) {
|
||||||
req.authData.actor.type === ActorType.USER &&
|
|
||||||
req.authData.tokenVersionId
|
|
||||||
) {
|
|
||||||
await clearTokens(req.authData.tokenVersionId);
|
await clearTokens(req.authData.tokenVersionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,8 +253,9 @@ export const createBackupPrivateKey = async (req: Request, res: Response) => {
|
|||||||
// create/change backup private key
|
// create/change backup private key
|
||||||
// requires verifying [clientProof] as part of second step of SRP protocol
|
// requires verifying [clientProof] as part of second step of SRP protocol
|
||||||
// as initiated in /srp1
|
// as initiated in /srp1
|
||||||
|
const {
|
||||||
const { clientProof, encryptedPrivateKey, iv, tag, salt, verifier } = req.body;
|
body: { clientProof, encryptedPrivateKey, salt, verifier, iv, tag }
|
||||||
|
} = await validateRequest(reqValidator.CreateBackupPrivateKeyV1, req);
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email: req.user.email
|
email: req.user.email
|
||||||
}).select("+salt +verifier");
|
}).select("+salt +verifier");
|
||||||
@@ -325,15 +333,17 @@ export const getBackupPrivateKey = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
export const resetPassword = async (req: Request, res: Response) => {
|
export const resetPassword = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
protectedKey,
|
body: {
|
||||||
protectedKeyIV,
|
encryptedPrivateKey,
|
||||||
protectedKeyTag,
|
protectedKeyTag,
|
||||||
encryptedPrivateKey,
|
protectedKey,
|
||||||
encryptedPrivateKeyIV,
|
protectedKeyIV,
|
||||||
encryptedPrivateKeyTag,
|
salt,
|
||||||
salt,
|
verifier,
|
||||||
verifier
|
encryptedPrivateKeyIV,
|
||||||
} = req.body;
|
encryptedPrivateKeyTag
|
||||||
|
}
|
||||||
|
} = await validateRequest(reqValidator.ResetPasswordV1, req);
|
||||||
|
|
||||||
await User.findByIdAndUpdate(
|
await User.findByIdAndUpdate(
|
||||||
req.user._id.toString(),
|
req.user._id.toString(),
|
||||||
|
|||||||
@@ -1,15 +1,28 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { isValidScope, validateMembership } from "../../helpers";
|
import { isValidScope } from "../../helpers";
|
||||||
import { Folder, SecretImport, ServiceTokenData } from "../../models";
|
import { Folder, SecretImport, ServiceTokenData } from "../../models";
|
||||||
import { getAllImportedSecrets } from "../../services/SecretImportService";
|
import { getAllImportedSecrets } from "../../services/SecretImportService";
|
||||||
import { getFolderWithPathFromId } from "../../services/FolderService";
|
import { getFolderWithPathFromId } from "../../services/FolderService";
|
||||||
import { BadRequestError, ResourceNotFoundError,UnauthorizedRequestError } from "../../utils/errors";
|
import {
|
||||||
import { ADMIN, MEMBER } from "../../variables";
|
BadRequestError,
|
||||||
|
ResourceNotFoundError,
|
||||||
|
UnauthorizedRequestError
|
||||||
|
} from "../../utils/errors";
|
||||||
import { EEAuditLogService } from "../../ee/services";
|
import { EEAuditLogService } from "../../ee/services";
|
||||||
import { EventType } from "../../ee/models";
|
import { EventType } from "../../ee/models";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/secretImports";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
export const createSecretImport = async (req: Request, res: Response) => {
|
export const createSecretImport = async (req: Request, res: Response) => {
|
||||||
const { workspaceId, environment, folderId, secretImport } = req.body;
|
const {
|
||||||
|
body: { workspaceId, environment, folderId, secretImport }
|
||||||
|
} = await validateRequest(reqValidator.CreateSecretImportV1, req);
|
||||||
|
|
||||||
const folders = await Folder.findOne({
|
const folders = await Folder.findOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
@@ -27,12 +40,19 @@ export const createSecretImport = async (req: Request, res: Response) => {
|
|||||||
const { folderPath } = getFolderWithPathFromId(folders.nodes, folderId);
|
const { folderPath } = getFolderWithPathFromId(folders.nodes, folderId);
|
||||||
secretPath = folderPath;
|
secretPath = folderPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.authData.authPayload instanceof ServiceTokenData) {
|
if (req.authData.authPayload instanceof ServiceTokenData) {
|
||||||
// root check
|
// root check
|
||||||
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, secretPath);
|
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, secretPath);
|
||||||
if (!isValidScopeAccess) {
|
if (!isValidScopeAccess) {
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
subject(ProjectPermissionSub.SecretImports, { environment, secretPath })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const importSecDoc = await SecretImport.findOne({
|
const importSecDoc = await SecretImport.findOne({
|
||||||
@@ -41,7 +61,9 @@ export const createSecretImport = async (req: Request, res: Response) => {
|
|||||||
folderId
|
folderId
|
||||||
});
|
});
|
||||||
|
|
||||||
const importToSecretPath = folders?getFolderWithPathFromId(folders.nodes, folderId).folderPath:"/";
|
const importToSecretPath = folders
|
||||||
|
? getFolderWithPathFromId(folders.nodes, folderId).folderPath
|
||||||
|
: "/";
|
||||||
|
|
||||||
if (!importSecDoc) {
|
if (!importSecDoc) {
|
||||||
const doc = new SecretImport({
|
const doc = new SecretImport({
|
||||||
@@ -108,32 +130,30 @@ export const createSecretImport = async (req: Request, res: Response) => {
|
|||||||
// to keep the ordering, you must pass all the imports in here not the only updated one
|
// to keep the ordering, you must pass all the imports in here not the only updated one
|
||||||
// this is because the order decide which import gets overriden
|
// this is because the order decide which import gets overriden
|
||||||
export const updateSecretImport = async (req: Request, res: Response) => {
|
export const updateSecretImport = async (req: Request, res: Response) => {
|
||||||
const { id } = req.params;
|
const {
|
||||||
const { secretImports } = req.body;
|
body: { secretImports },
|
||||||
|
params: { id }
|
||||||
|
} = await validateRequest(reqValidator.UpdateSecretImportV1, req);
|
||||||
|
|
||||||
const importSecDoc = await SecretImport.findById(id);
|
const importSecDoc = await SecretImport.findById(id);
|
||||||
if (!importSecDoc) {
|
if (!importSecDoc) {
|
||||||
throw BadRequestError({ message: "Import not found" });
|
throw BadRequestError({ message: "Import not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(req.authData.authPayload instanceof ServiceTokenData)) {
|
// check for service token validity
|
||||||
await validateMembership({
|
const folders = await Folder.findOne({
|
||||||
userId: req.user._id.toString(),
|
workspace: importSecDoc.workspace,
|
||||||
workspaceId: importSecDoc.workspace,
|
environment: importSecDoc.environment
|
||||||
acceptedRoles: [ADMIN, MEMBER]
|
}).lean();
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// check for service token validity
|
|
||||||
const folders = await Folder.findOne({
|
|
||||||
workspace: importSecDoc.workspace,
|
|
||||||
environment: importSecDoc.environment
|
|
||||||
}).lean();
|
|
||||||
|
|
||||||
let secretPath = "/";
|
let secretPath = "/";
|
||||||
if (folders) {
|
if (folders) {
|
||||||
const { folderPath } = getFolderWithPathFromId(folders.nodes, importSecDoc.folderId);
|
const { folderPath } = getFolderWithPathFromId(folders.nodes, importSecDoc.folderId);
|
||||||
secretPath = folderPath;
|
secretPath = folderPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req.authData.authPayload instanceof ServiceTokenData) {
|
||||||
|
// token permission check
|
||||||
const isValidScopeAccess = isValidScope(
|
const isValidScopeAccess = isValidScope(
|
||||||
req.authData.authPayload,
|
req.authData.authPayload,
|
||||||
importSecDoc.environment,
|
importSecDoc.environment,
|
||||||
@@ -142,6 +162,19 @@ export const updateSecretImport = async (req: Request, res: Response) => {
|
|||||||
if (!isValidScopeAccess) {
|
if (!isValidScopeAccess) {
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// non token entry check
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
importSecDoc.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
subject(ProjectPermissionSub.SecretImports, {
|
||||||
|
environment: importSecDoc.environment,
|
||||||
|
secretPath
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const orderBefore = importSecDoc.imports;
|
const orderBefore = importSecDoc.imports;
|
||||||
@@ -149,24 +182,13 @@ export const updateSecretImport = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
await importSecDoc.save();
|
await importSecDoc.save();
|
||||||
|
|
||||||
const folders = await Folder.findOne({
|
|
||||||
workspace: importSecDoc.workspace,
|
|
||||||
environment: importSecDoc.environment,
|
|
||||||
}).lean();
|
|
||||||
|
|
||||||
if (!folders) throw ResourceNotFoundError({
|
|
||||||
message: "Failed to find folder"
|
|
||||||
});
|
|
||||||
|
|
||||||
const importToSecretPath = folders?getFolderWithPathFromId(folders.nodes, importSecDoc.folderId).folderPath:"/";
|
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
req.authData,
|
req.authData,
|
||||||
{
|
{
|
||||||
type: EventType.UPDATE_SECRET_IMPORT,
|
type: EventType.UPDATE_SECRET_IMPORT,
|
||||||
metadata: {
|
metadata: {
|
||||||
importToEnvironment: importSecDoc.environment,
|
importToEnvironment: importSecDoc.environment,
|
||||||
importToSecretPath,
|
importToSecretPath: secretPath,
|
||||||
secretImportId: importSecDoc._id.toString(),
|
secretImportId: importSecDoc._id.toString(),
|
||||||
folderId: importSecDoc.folderId.toString(),
|
folderId: importSecDoc.folderId.toString(),
|
||||||
orderBefore,
|
orderBefore,
|
||||||
@@ -181,32 +203,29 @@ export const updateSecretImport = async (req: Request, res: Response) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const deleteSecretImport = async (req: Request, res: Response) => {
|
export const deleteSecretImport = async (req: Request, res: Response) => {
|
||||||
const { id } = req.params;
|
const {
|
||||||
const { secretImportEnv, secretImportPath } = req.body;
|
params: { id },
|
||||||
|
body: { secretImportEnv, secretImportPath }
|
||||||
|
} = await validateRequest(reqValidator.DeleteSecretImportV1, req);
|
||||||
|
|
||||||
const importSecDoc = await SecretImport.findById(id);
|
const importSecDoc = await SecretImport.findById(id);
|
||||||
if (!importSecDoc) {
|
if (!importSecDoc) {
|
||||||
throw BadRequestError({ message: "Import not found" });
|
throw BadRequestError({ message: "Import not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(req.authData.authPayload instanceof ServiceTokenData)) {
|
// check for service token validity
|
||||||
await validateMembership({
|
const folders = await Folder.findOne({
|
||||||
userId: req.user._id.toString(),
|
workspace: importSecDoc.workspace,
|
||||||
workspaceId: importSecDoc.workspace,
|
environment: importSecDoc.environment
|
||||||
acceptedRoles: [ADMIN, MEMBER]
|
}).lean();
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// check for service token validity
|
|
||||||
const folders = await Folder.findOne({
|
|
||||||
workspace: importSecDoc.workspace,
|
|
||||||
environment: importSecDoc.environment
|
|
||||||
}).lean();
|
|
||||||
|
|
||||||
let secretPath = "/";
|
let secretPath = "/";
|
||||||
if (folders) {
|
if (folders) {
|
||||||
const { folderPath } = getFolderWithPathFromId(folders.nodes, importSecDoc.folderId);
|
const { folderPath } = getFolderWithPathFromId(folders.nodes, importSecDoc.folderId);
|
||||||
secretPath = folderPath;
|
secretPath = folderPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req.authData.authPayload instanceof ServiceTokenData) {
|
||||||
const isValidScopeAccess = isValidScope(
|
const isValidScopeAccess = isValidScope(
|
||||||
req.authData.authPayload,
|
req.authData.authPayload,
|
||||||
importSecDoc.environment,
|
importSecDoc.environment,
|
||||||
@@ -215,6 +234,18 @@ export const deleteSecretImport = async (req: Request, res: Response) => {
|
|||||||
if (!isValidScopeAccess) {
|
if (!isValidScopeAccess) {
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
importSecDoc.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
subject(ProjectPermissionSub.SecretImports, {
|
||||||
|
environment: importSecDoc.environment,
|
||||||
|
secretPath
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
importSecDoc.imports = importSecDoc.imports.filter(
|
importSecDoc.imports = importSecDoc.imports.filter(
|
||||||
({ environment, secretPath }) =>
|
({ environment, secretPath }) =>
|
||||||
@@ -222,17 +253,6 @@ export const deleteSecretImport = async (req: Request, res: Response) => {
|
|||||||
);
|
);
|
||||||
await importSecDoc.save();
|
await importSecDoc.save();
|
||||||
|
|
||||||
const folders = await Folder.findOne({
|
|
||||||
workspace: importSecDoc.workspace,
|
|
||||||
environment: importSecDoc.environment,
|
|
||||||
}).lean();
|
|
||||||
|
|
||||||
if (!folders) throw ResourceNotFoundError({
|
|
||||||
message: "Failed to find folder"
|
|
||||||
});
|
|
||||||
|
|
||||||
const importToSecretPath = folders?getFolderWithPathFromId(folders.nodes, importSecDoc.folderId).folderPath:"/";
|
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
req.authData,
|
req.authData,
|
||||||
{
|
{
|
||||||
@@ -243,7 +263,7 @@ export const deleteSecretImport = async (req: Request, res: Response) => {
|
|||||||
importFromEnvironment: secretImportEnv,
|
importFromEnvironment: secretImportEnv,
|
||||||
importFromSecretPath: secretImportPath,
|
importFromSecretPath: secretImportPath,
|
||||||
importToEnvironment: importSecDoc.environment,
|
importToEnvironment: importSecDoc.environment,
|
||||||
importToSecretPath
|
importToSecretPath: secretPath
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -266,19 +286,19 @@ export const getSecretImports = async (req: Request, res: Response) => {
|
|||||||
return res.status(200).json({ secretImport: {} });
|
return res.status(200).json({ secretImport: {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for service token validity
|
||||||
|
const folders = await Folder.findOne({
|
||||||
|
workspace: importSecDoc.workspace,
|
||||||
|
environment: importSecDoc.environment
|
||||||
|
}).lean();
|
||||||
|
|
||||||
|
let secretPath = "/";
|
||||||
|
if (folders) {
|
||||||
|
const { folderPath } = getFolderWithPathFromId(folders.nodes, importSecDoc.folderId);
|
||||||
|
secretPath = folderPath;
|
||||||
|
}
|
||||||
|
|
||||||
if (req.authData.authPayload instanceof ServiceTokenData) {
|
if (req.authData.authPayload instanceof ServiceTokenData) {
|
||||||
// check for service token validity
|
|
||||||
const folders = await Folder.findOne({
|
|
||||||
workspace: importSecDoc.workspace,
|
|
||||||
environment: importSecDoc.environment
|
|
||||||
}).lean();
|
|
||||||
|
|
||||||
let secretPath = "/";
|
|
||||||
if (folders) {
|
|
||||||
const { folderPath } = getFolderWithPathFromId(folders.nodes, importSecDoc.folderId);
|
|
||||||
secretPath = folderPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isValidScopeAccess = isValidScope(
|
const isValidScopeAccess = isValidScope(
|
||||||
req.authData.authPayload,
|
req.authData.authPayload,
|
||||||
importSecDoc.environment,
|
importSecDoc.environment,
|
||||||
@@ -287,6 +307,18 @@ export const getSecretImports = async (req: Request, res: Response) => {
|
|||||||
if (!isValidScopeAccess) {
|
if (!isValidScopeAccess) {
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
importSecDoc.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.SecretImports, {
|
||||||
|
environment: importSecDoc.environment,
|
||||||
|
secretPath
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).json({ secretImport: importSecDoc });
|
return res.status(200).json({ secretImport: importSecDoc });
|
||||||
@@ -308,19 +340,19 @@ export const getAllSecretsFromImport = async (req: Request, res: Response) => {
|
|||||||
return res.status(200).json({ secrets: [] });
|
return res.status(200).json({ secrets: [] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const folders = await Folder.findOne({
|
||||||
|
workspace: importSecDoc.workspace,
|
||||||
|
environment: importSecDoc.environment
|
||||||
|
}).lean();
|
||||||
|
|
||||||
|
let secretPath = "/";
|
||||||
|
if (folders) {
|
||||||
|
const { folderPath } = getFolderWithPathFromId(folders.nodes, importSecDoc.folderId);
|
||||||
|
secretPath = folderPath;
|
||||||
|
}
|
||||||
|
|
||||||
if (req.authData.authPayload instanceof ServiceTokenData) {
|
if (req.authData.authPayload instanceof ServiceTokenData) {
|
||||||
// check for service token validity
|
// check for service token validity
|
||||||
const folders = await Folder.findOne({
|
|
||||||
workspace: importSecDoc.workspace,
|
|
||||||
environment: importSecDoc.environment
|
|
||||||
}).lean();
|
|
||||||
|
|
||||||
let secretPath = "/";
|
|
||||||
if (folders) {
|
|
||||||
const { folderPath } = getFolderWithPathFromId(folders.nodes, importSecDoc.folderId);
|
|
||||||
secretPath = folderPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isValidScopeAccess = isValidScope(
|
const isValidScopeAccess = isValidScope(
|
||||||
req.authData.authPayload,
|
req.authData.authPayload,
|
||||||
importSecDoc.environment,
|
importSecDoc.environment,
|
||||||
@@ -329,6 +361,25 @@ export const getAllSecretsFromImport = async (req: Request, res: Response) => {
|
|||||||
if (!isValidScopeAccess) {
|
if (!isValidScopeAccess) {
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
importSecDoc.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.SecretImports, {
|
||||||
|
environment: importSecDoc.environment,
|
||||||
|
secretPath
|
||||||
|
})
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.Secrets, {
|
||||||
|
environment: importSecDoc.environment,
|
||||||
|
secretPath
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { EventType, FolderVersion } from "../../ee/models";
|
import { EventType, FolderVersion } from "../../ee/models";
|
||||||
import { EEAuditLogService, EESecretService } from "../../ee/services";
|
import { EEAuditLogService, EESecretService } from "../../ee/services";
|
||||||
import { validateMembership } from "../../helpers/membership";
|
|
||||||
import { isValidScope } from "../../helpers/secrets";
|
import { isValidScope } from "../../helpers/secrets";
|
||||||
import { Folder, Secret, ServiceTokenData } from "../../models";
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import { Secret, ServiceTokenData } from "../../models";
|
||||||
|
import {Folder} from "../../models/folder";
|
||||||
import {
|
import {
|
||||||
appendFolder,
|
appendFolder,
|
||||||
deleteFolderById,
|
deleteFolderById,
|
||||||
@@ -15,12 +17,20 @@ import {
|
|||||||
getParentFromFolderId,
|
getParentFromFolderId,
|
||||||
validateFolderName
|
validateFolderName
|
||||||
} from "../../services/FolderService";
|
} from "../../services/FolderService";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
||||||
import { ADMIN, MEMBER } from "../../variables";
|
import * as reqValidator from "../../validation/folders";
|
||||||
|
|
||||||
// verify workspace id/environment
|
// verify workspace id/environment
|
||||||
export const createFolder = async (req: Request, res: Response) => {
|
export const createFolder = async (req: Request, res: Response) => {
|
||||||
const { workspaceId, environment, folderName, parentFolderId } = req.body;
|
const {
|
||||||
|
body: { workspaceId, environment, folderName, parentFolderId }
|
||||||
|
} = await validateRequest(reqValidator.CreateFolderV1, req);
|
||||||
|
|
||||||
if (!validateFolderName(folderName)) {
|
if (!validateFolderName(folderName)) {
|
||||||
throw BadRequestError({
|
throw BadRequestError({
|
||||||
message: "Folder name cannot contain spaces. Only underscore and dashes"
|
message: "Folder name cannot contain spaces. Only underscore and dashes"
|
||||||
@@ -32,6 +42,18 @@ export const createFolder = async (req: Request, res: Response) => {
|
|||||||
environment
|
environment
|
||||||
}).lean();
|
}).lean();
|
||||||
|
|
||||||
|
if (req.user) {
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
const secretPath =
|
||||||
|
folders && parentFolderId
|
||||||
|
? getFolderWithPathFromId(folders.nodes, parentFolderId).folderPath
|
||||||
|
: "/";
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
subject(ProjectPermissionSub.Folders, { environment, secretPath })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// space has no folders initialized
|
// space has no folders initialized
|
||||||
|
|
||||||
if (!folders) {
|
if (!folders) {
|
||||||
@@ -62,7 +84,7 @@ export const createFolder = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
await folderVersion.save();
|
await folderVersion.save();
|
||||||
await EESecretService.takeSecretSnapshot({
|
await EESecretService.takeSecretSnapshot({
|
||||||
workspaceId,
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment
|
environment
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -116,12 +138,12 @@ export const createFolder = async (req: Request, res: Response) => {
|
|||||||
await folderVersion.save();
|
await folderVersion.save();
|
||||||
|
|
||||||
await EESecretService.takeSecretSnapshot({
|
await EESecretService.takeSecretSnapshot({
|
||||||
workspaceId,
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
folderId: parentFolderId
|
folderId: parentFolderId
|
||||||
});
|
});
|
||||||
|
|
||||||
const {folderPath} = getFolderWithPathFromId(folders.nodes, folder.id);
|
const { folderPath } = getFolderWithPathFromId(folders.nodes, folder.id);
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
req.authData,
|
req.authData,
|
||||||
@@ -143,8 +165,11 @@ export const createFolder = async (req: Request, res: Response) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const updateFolderById = async (req: Request, res: Response) => {
|
export const updateFolderById = async (req: Request, res: Response) => {
|
||||||
const { folderId } = req.params;
|
const {
|
||||||
const { name, workspaceId, environment } = req.body;
|
body: { workspaceId, environment, name },
|
||||||
|
params: { folderId }
|
||||||
|
} = await validateRequest(reqValidator.UpdateFolderV1, req);
|
||||||
|
|
||||||
if (!validateFolderName(name)) {
|
if (!validateFolderName(name)) {
|
||||||
throw BadRequestError({
|
throw BadRequestError({
|
||||||
message: "Folder name cannot contain spaces. Only underscore and dashes"
|
message: "Folder name cannot contain spaces. Only underscore and dashes"
|
||||||
@@ -156,21 +181,21 @@ export const updateFolderById = async (req: Request, res: Response) => {
|
|||||||
throw BadRequestError({ message: "The folder doesn't exist" });
|
throw BadRequestError({ message: "The folder doesn't exist" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(req.authData.authPayload instanceof ServiceTokenData)) {
|
|
||||||
// check that user is a member of the workspace
|
|
||||||
await validateMembership({
|
|
||||||
userId: req.user._id.toString(),
|
|
||||||
workspaceId,
|
|
||||||
acceptedRoles: [ADMIN, MEMBER]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentFolder = getParentFromFolderId(folders.nodes, folderId);
|
const parentFolder = getParentFromFolderId(folders.nodes, folderId);
|
||||||
if (!parentFolder) {
|
if (!parentFolder) {
|
||||||
throw BadRequestError({ message: "The folder doesn't exist" });
|
throw BadRequestError({ message: "The folder doesn't exist" });
|
||||||
}
|
}
|
||||||
const folder = parentFolder.children.find(({ id }) => id === folderId);
|
|
||||||
|
|
||||||
|
if (req.user) {
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
const secretPath = getFolderWithPathFromId(folders.nodes, parentFolder.id).folderPath;
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
subject(ProjectPermissionSub.Folders, { environment, secretPath })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const folder = parentFolder.children.find(({ id }) => id === folderId);
|
||||||
if (!folder) {
|
if (!folder) {
|
||||||
throw BadRequestError({ message: "The folder doesn't exist" });
|
throw BadRequestError({ message: "The folder doesn't exist" });
|
||||||
}
|
}
|
||||||
@@ -197,12 +222,12 @@ export const updateFolderById = async (req: Request, res: Response) => {
|
|||||||
await folderVersion.save();
|
await folderVersion.save();
|
||||||
|
|
||||||
await EESecretService.takeSecretSnapshot({
|
await EESecretService.takeSecretSnapshot({
|
||||||
workspaceId,
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
folderId: parentFolder.id
|
folderId: parentFolder.id
|
||||||
});
|
});
|
||||||
|
|
||||||
const {folderPath} = getFolderWithPathFromId(folders.nodes, folder.id);
|
const { folderPath } = getFolderWithPathFromId(folders.nodes, folder.id);
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
req.authData,
|
req.authData,
|
||||||
@@ -228,37 +253,35 @@ export const updateFolderById = async (req: Request, res: Response) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const deleteFolder = async (req: Request, res: Response) => {
|
export const deleteFolder = async (req: Request, res: Response) => {
|
||||||
const { folderId } = req.params;
|
const {
|
||||||
const { workspaceId, environment } = req.body;
|
params: { folderId },
|
||||||
|
body: { environment, workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.DeleteFolderV1, req);
|
||||||
|
|
||||||
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
||||||
if (!folders) {
|
if (!folders) {
|
||||||
throw BadRequestError({ message: "The folder doesn't exist" });
|
throw BadRequestError({ message: "The folder doesn't exist" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(req.authData.authPayload instanceof ServiceTokenData)) {
|
|
||||||
// check that user is a member of the workspace
|
|
||||||
await validateMembership({
|
|
||||||
userId: req.user._id.toString(),
|
|
||||||
workspaceId,
|
|
||||||
acceptedRoles: [ADMIN, MEMBER]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const {folderPath} = getFolderWithPathFromId(folders.nodes, folderId);
|
|
||||||
|
|
||||||
const delOp = deleteFolderById(folders.nodes, folderId);
|
const delOp = deleteFolderById(folders.nodes, folderId);
|
||||||
if (!delOp) {
|
if (!delOp) {
|
||||||
throw BadRequestError({ message: "The folder doesn't exist" });
|
throw BadRequestError({ message: "The folder doesn't exist" });
|
||||||
}
|
}
|
||||||
const { deletedNode: delFolder, parent: parentFolder } = delOp;
|
const { deletedNode: delFolder, parent: parentFolder } = delOp;
|
||||||
|
const { folderPath: secretPath } = getFolderWithPathFromId(folders.nodes, parentFolder.id);
|
||||||
|
|
||||||
if (req.authData.authPayload instanceof ServiceTokenData) {
|
if (req.authData.authPayload instanceof ServiceTokenData) {
|
||||||
const { folderPath: secretPath } = getFolderWithPathFromId(folders.nodes, parentFolder.id);
|
|
||||||
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, secretPath);
|
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, secretPath);
|
||||||
if (!isValidScopeAccess) {
|
if (!isValidScopeAccess) {
|
||||||
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// check that user is a member of the workspace
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
subject(ProjectPermissionSub.Folders, { environment, secretPath })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
parentFolder.version += 1;
|
parentFolder.version += 1;
|
||||||
@@ -280,7 +303,7 @@ export const deleteFolder = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await EESecretService.takeSecretSnapshot({
|
await EESecretService.takeSecretSnapshot({
|
||||||
workspaceId,
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
folderId: parentFolder.id
|
folderId: parentFolder.id
|
||||||
});
|
});
|
||||||
@@ -288,12 +311,12 @@ export const deleteFolder = async (req: Request, res: Response) => {
|
|||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
req.authData,
|
req.authData,
|
||||||
{
|
{
|
||||||
type: EventType.DELETE_FOLDER ,
|
type: EventType.DELETE_FOLDER,
|
||||||
metadata: {
|
metadata: {
|
||||||
environment,
|
environment,
|
||||||
folderId,
|
folderId,
|
||||||
folderName: delFolder.name,
|
folderName: delFolder.name,
|
||||||
folderPath
|
folderPath: secretPath
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -306,28 +329,30 @@ export const deleteFolder = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
// TODO: validate workspace
|
// TODO: validate workspace
|
||||||
export const getFolders = async (req: Request, res: Response) => {
|
export const getFolders = async (req: Request, res: Response) => {
|
||||||
const { workspaceId, environment, parentFolderId, parentFolderPath } = req.query as {
|
const {
|
||||||
workspaceId: string;
|
query: { workspaceId, environment, parentFolderId, parentFolderPath }
|
||||||
environment: string;
|
} = await validateRequest(reqValidator.GetFoldersV1, req);
|
||||||
parentFolderId?: string;
|
|
||||||
parentFolderPath?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
||||||
|
|
||||||
|
if (req.user) {
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
const secretPath =
|
||||||
|
folders && parentFolderId
|
||||||
|
? getFolderWithPathFromId(folders.nodes, parentFolderId).folderPath
|
||||||
|
: parentFolderPath || "/";
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.Folders, { environment, secretPath })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!folders) {
|
if (!folders) {
|
||||||
res.send({ folders: [], dir: [] });
|
res.send({ folders: [], dir: [] });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(req.authData.authPayload instanceof ServiceTokenData)) {
|
|
||||||
// check that user is a member of the workspace
|
|
||||||
await validateMembership({
|
|
||||||
userId: req.user._id.toString(),
|
|
||||||
workspaceId,
|
|
||||||
acceptedRoles: [ADMIN, MEMBER]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// if instead of parentFolderId given a path like /folder1/folder2
|
// if instead of parentFolderId given a path like /folder1/folder2
|
||||||
if (parentFolderPath) {
|
if (parentFolderPath) {
|
||||||
if (req.authData.authPayload instanceof ServiceTokenData) {
|
if (req.authData.authPayload instanceof ServiceTokenData) {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import {
|
|||||||
getSmtpConfigured
|
getSmtpConfigured
|
||||||
} from "../../config";
|
} from "../../config";
|
||||||
import { validateUserEmail } from "../../validation";
|
import { validateUserEmail } from "../../validation";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/auth";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signup step 1: Initialize account for user under email [email] and send a verification code
|
* Signup step 1: Initialize account for user under email [email] and send a verification code
|
||||||
@@ -19,7 +21,9 @@ import { validateUserEmail } from "../../validation";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const beginEmailSignup = async (req: Request, res: Response) => {
|
export const beginEmailSignup = async (req: Request, res: Response) => {
|
||||||
const email: string = req.body.email;
|
const {
|
||||||
|
body: { email }
|
||||||
|
} = await validateRequest(reqValidator.BeginEmailSignUpV1, req);
|
||||||
|
|
||||||
// validate that email is not disposable
|
// validate that email is not disposable
|
||||||
validateUserEmail(email);
|
validateUserEmail(email);
|
||||||
@@ -50,7 +54,9 @@ export const beginEmailSignup = async (req: Request, res: Response) => {
|
|||||||
*/
|
*/
|
||||||
export const verifyEmailSignup = async (req: Request, res: Response) => {
|
export const verifyEmailSignup = async (req: Request, res: Response) => {
|
||||||
let user;
|
let user;
|
||||||
const { email, code } = req.body;
|
const {
|
||||||
|
body: { email, code }
|
||||||
|
} = await validateRequest(reqValidator.VerifyEmailSignUpV1, req);
|
||||||
|
|
||||||
// initialize user account
|
// initialize user account
|
||||||
user = await User.findOne({ email }).select("+publicKey");
|
user = await User.findOne({ email }).select("+publicKey");
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
import { UserAction } from "../../models";
|
import { UserAction } from "../../models";
|
||||||
|
import * as reqValidator from "../../validation/action";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add user action [action]
|
* Add user action [action]
|
||||||
@@ -8,26 +10,27 @@ import { UserAction } from "../../models";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const addUserAction = async (req: Request, res: Response) => {
|
export const addUserAction = async (req: Request, res: Response) => {
|
||||||
// add/record new action [action] for user with id [req.user._id]
|
// add/record new action [action] for user with id [req.user._id]
|
||||||
|
const {
|
||||||
const { action } = req.body;
|
body: { action }
|
||||||
|
} = await validateRequest(reqValidator.AddUserActionV1, req);
|
||||||
|
|
||||||
const userAction = await UserAction.findOneAndUpdate(
|
const userAction = await UserAction.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
user: req.user._id,
|
user: req.user._id,
|
||||||
action,
|
action
|
||||||
},
|
},
|
||||||
{ user: req.user._id, action },
|
{ user: req.user._id, action },
|
||||||
{
|
{
|
||||||
new: true,
|
new: true,
|
||||||
upsert: true,
|
upsert: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: "Successfully recorded user action",
|
message: "Successfully recorded user action",
|
||||||
userAction,
|
userAction
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,15 +40,17 @@ export const addUserAction = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getUserAction = async (req: Request, res: Response) => {
|
export const getUserAction = async (req: Request, res: Response) => {
|
||||||
// get user action [action] for user with id [req.user._id]
|
// get user action [action] for user with id [req.user._id]
|
||||||
const action: string = req.query.action as string;
|
const {
|
||||||
|
query: { action }
|
||||||
|
} = await validateRequest(reqValidator.GetUserActionV1, req);
|
||||||
|
|
||||||
const userAction = await UserAction.findOne({
|
const userAction = await UserAction.findOne({
|
||||||
user: req.user._id,
|
user: req.user._id,
|
||||||
action,
|
action
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
userAction,
|
userAction
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,16 +1,32 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { client, getRootEncryptionKey } from "../../config";
|
import { client, getRootEncryptionKey } from "../../config";
|
||||||
import { validateMembership } from "../../helpers";
|
|
||||||
import { Webhook } from "../../models";
|
import { Webhook } from "../../models";
|
||||||
import { getWebhookPayload, triggerWebhookRequest } from "../../services/WebhookService";
|
import { getWebhookPayload, triggerWebhookRequest } from "../../services/WebhookService";
|
||||||
import { BadRequestError, ResourceNotFoundError } from "../../utils/errors";
|
import { BadRequestError, ResourceNotFoundError } from "../../utils/errors";
|
||||||
import { EEAuditLogService } from "../../ee/services";
|
import { EEAuditLogService } from "../../ee/services";
|
||||||
import { EventType } from "../../ee/models";
|
import { EventType } from "../../ee/models";
|
||||||
import { ADMIN, ALGORITHM_AES_256_GCM, ENCODING_SCHEME_BASE64, MEMBER } from "../../variables";
|
import { ALGORITHM_AES_256_GCM, ENCODING_SCHEME_BASE64 } from "../../variables";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/webhooks";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
export const createWebhook = async (req: Request, res: Response) => {
|
export const createWebhook = async (req: Request, res: Response) => {
|
||||||
const { webhookUrl, webhookSecretKey, environment, workspaceId, secretPath } = req.body;
|
const {
|
||||||
|
body: { webhookUrl, webhookSecretKey, environment, workspaceId, secretPath }
|
||||||
|
} = await validateRequest(reqValidator.CreateWebhookV1, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionSub.Webhooks
|
||||||
|
);
|
||||||
|
|
||||||
const webhook = new Webhook({
|
const webhook = new Webhook({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment,
|
environment,
|
||||||
@@ -43,7 +59,7 @@ export const createWebhook = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
workspaceId
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -54,19 +70,24 @@ export const createWebhook = async (req: Request, res: Response) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const updateWebhook = async (req: Request, res: Response) => {
|
export const updateWebhook = async (req: Request, res: Response) => {
|
||||||
const { webhookId } = req.params;
|
const {
|
||||||
const { isDisabled } = req.body;
|
body: { isDisabled },
|
||||||
|
params: { webhookId }
|
||||||
|
} = await validateRequest(reqValidator.UpdateWebhookV1, req);
|
||||||
|
|
||||||
const webhook = await Webhook.findById(webhookId);
|
const webhook = await Webhook.findById(webhookId);
|
||||||
if (!webhook) {
|
if (!webhook) {
|
||||||
throw BadRequestError({ message: "Webhook not found!!" });
|
throw BadRequestError({ message: "Webhook not found!!" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that user is a member of the workspace
|
const { permission } = await getUserProjectPermissions(
|
||||||
await validateMembership({
|
req.user._id,
|
||||||
userId: req.user._id.toString(),
|
webhook.workspace.toString()
|
||||||
workspaceId: webhook.workspace,
|
);
|
||||||
acceptedRoles: [ADMIN, MEMBER]
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
});
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionSub.Webhooks
|
||||||
|
);
|
||||||
|
|
||||||
if (typeof isDisabled !== undefined) {
|
if (typeof isDisabled !== undefined) {
|
||||||
webhook.isDisabled = isDisabled;
|
webhook.isDisabled = isDisabled;
|
||||||
@@ -97,18 +118,23 @@ export const updateWebhook = async (req: Request, res: Response) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const deleteWebhook = async (req: Request, res: Response) => {
|
export const deleteWebhook = async (req: Request, res: Response) => {
|
||||||
const { webhookId } = req.params;
|
const {
|
||||||
|
params: { webhookId }
|
||||||
|
} = await validateRequest(reqValidator.DeleteWebhookV1, req);
|
||||||
let webhook = await Webhook.findById(webhookId);
|
let webhook = await Webhook.findById(webhookId);
|
||||||
|
|
||||||
if (!webhook) {
|
if (!webhook) {
|
||||||
throw ResourceNotFoundError({ message: "Webhook not found!!" });
|
throw ResourceNotFoundError({ message: "Webhook not found!!" });
|
||||||
}
|
}
|
||||||
|
|
||||||
await validateMembership({
|
const { permission } = await getUserProjectPermissions(
|
||||||
userId: req.user._id.toString(),
|
req.user._id,
|
||||||
workspaceId: webhook.workspace,
|
webhook.workspace.toString()
|
||||||
acceptedRoles: [ADMIN, MEMBER]
|
);
|
||||||
});
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
ProjectPermissionSub.Webhooks
|
||||||
|
);
|
||||||
|
|
||||||
webhook = await Webhook.findByIdAndDelete(webhookId);
|
webhook = await Webhook.findByIdAndDelete(webhookId);
|
||||||
|
|
||||||
@@ -139,17 +165,23 @@ export const deleteWebhook = async (req: Request, res: Response) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const testWebhook = async (req: Request, res: Response) => {
|
export const testWebhook = async (req: Request, res: Response) => {
|
||||||
const { webhookId } = req.params;
|
const {
|
||||||
|
params: { webhookId }
|
||||||
|
} = await validateRequest(reqValidator.TestWebhookV1, req);
|
||||||
|
|
||||||
const webhook = await Webhook.findById(webhookId);
|
const webhook = await Webhook.findById(webhookId);
|
||||||
if (!webhook) {
|
if (!webhook) {
|
||||||
throw BadRequestError({ message: "Webhook not found!!" });
|
throw BadRequestError({ message: "Webhook not found!!" });
|
||||||
}
|
}
|
||||||
|
|
||||||
await validateMembership({
|
const { permission } = await getUserProjectPermissions(
|
||||||
userId: req.user._id.toString(),
|
req.user._id,
|
||||||
workspaceId: webhook.workspace,
|
webhook.workspace.toString()
|
||||||
acceptedRoles: [ADMIN, MEMBER]
|
);
|
||||||
});
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Webhooks
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await triggerWebhookRequest(
|
await triggerWebhookRequest(
|
||||||
@@ -182,7 +214,15 @@ export const testWebhook = async (req: Request, res: Response) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const listWebhooks = async (req: Request, res: Response) => {
|
export const listWebhooks = async (req: Request, res: Response) => {
|
||||||
const { environment, workspaceId, secretPath } = req.query;
|
const {
|
||||||
|
query: { environment, workspaceId, secretPath }
|
||||||
|
} = await validateRequest(reqValidator.ListWebhooksV1, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Webhooks
|
||||||
|
);
|
||||||
|
|
||||||
const optionalFilters: Record<string, string> = {};
|
const optionalFilters: Record<string, string> = {};
|
||||||
if (environment) optionalFilters.environment = environment as string;
|
if (environment) optionalFilters.environment = environment as string;
|
||||||
|
|||||||
@@ -20,6 +20,13 @@ import {
|
|||||||
getUserOrgPermissions
|
getUserOrgPermissions
|
||||||
} from "../../services/RoleService";
|
} from "../../services/RoleService";
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return public keys of members of workspace with id [workspaceId]
|
* Return public keys of members of workspace with id [workspaceId]
|
||||||
@@ -28,7 +35,15 @@ import { ForbiddenError } from "@casl/ability";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getWorkspacePublicKeys = async (req: Request, res: Response) => {
|
export const getWorkspacePublicKeys = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetWorkspacePublicKeysV1, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Member
|
||||||
|
);
|
||||||
|
|
||||||
const publicKeys = (
|
const publicKeys = (
|
||||||
await Membership.find({
|
await Membership.find({
|
||||||
@@ -53,7 +68,15 @@ export const getWorkspacePublicKeys = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getWorkspaceMemberships = async (req: Request, res: Response) => {
|
export const getWorkspaceMemberships = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetWorkspaceMembershipsV1, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Member
|
||||||
|
);
|
||||||
|
|
||||||
const users = await Membership.find({
|
const users = await Membership.find({
|
||||||
workspace: workspaceId
|
workspace: workspaceId
|
||||||
@@ -89,7 +112,9 @@ export const getWorkspaces = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getWorkspace = async (req: Request, res: Response) => {
|
export const getWorkspace = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetWorkspaceV1, req);
|
||||||
|
|
||||||
const workspace = await Workspace.findOne({
|
const workspace = await Workspace.findOne({
|
||||||
_id: workspaceId
|
_id: workspaceId
|
||||||
@@ -108,7 +133,9 @@ export const getWorkspace = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const createWorkspace = async (req: Request, res: Response) => {
|
export const createWorkspace = async (req: Request, res: Response) => {
|
||||||
const { workspaceName, organizationId } = req.body;
|
const {
|
||||||
|
body: { organizationId, workspaceName }
|
||||||
|
} = await validateRequest(reqValidator.CreateWorkspaceV1, req);
|
||||||
|
|
||||||
const organization = await Organization.findById(organizationId);
|
const organization = await Organization.findById(organizationId);
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -183,8 +210,16 @@ export const deleteWorkspace = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const changeWorkspaceName = async (req: Request, res: Response) => {
|
export const changeWorkspaceName = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
const { name } = req.body;
|
params: { workspaceId },
|
||||||
|
body: { name }
|
||||||
|
} = await validateRequest(reqValidator.ChangeWorkspaceNameV1, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionSub.Workspace
|
||||||
|
);
|
||||||
|
|
||||||
const workspace = await Workspace.findOneAndUpdate(
|
const workspace = await Workspace.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
@@ -229,7 +264,15 @@ export const getWorkspaceIntegrations = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getWorkspaceIntegrationAuthorizations = async (req: Request, res: Response) => {
|
export const getWorkspaceIntegrationAuthorizations = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetWorkspaceIntegrationAuthorizationsV1, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
const authorizations = await IntegrationAuth.find({
|
const authorizations = await IntegrationAuth.find({
|
||||||
workspace: workspaceId
|
workspace: workspaceId
|
||||||
@@ -247,7 +290,16 @@ export const getWorkspaceIntegrationAuthorizations = async (req: Request, res: R
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getWorkspaceServiceTokens = async (req: Request, res: Response) => {
|
export const getWorkspaceServiceTokens = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetWorkspaceServiceTokensV1, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.ServiceTokens
|
||||||
|
);
|
||||||
|
|
||||||
// ?? FIX.
|
// ?? FIX.
|
||||||
const serviceTokens = await ServiceToken.find({
|
const serviceTokens = await ServiceToken.find({
|
||||||
user: req.user._id,
|
user: req.user._id,
|
||||||
|
|||||||
@@ -10,16 +10,11 @@ import { sendMail } from "../../helpers/nodemailer";
|
|||||||
import { TokenService } from "../../services";
|
import { TokenService } from "../../services";
|
||||||
import { EELogService } from "../../ee/services";
|
import { EELogService } from "../../ee/services";
|
||||||
import { BadRequestError, InternalServerError } from "../../utils/errors";
|
import { BadRequestError, InternalServerError } from "../../utils/errors";
|
||||||
import {
|
import { ACTION_LOGIN, TOKEN_EMAIL_MFA } from "../../variables";
|
||||||
ACTION_LOGIN,
|
|
||||||
TOKEN_EMAIL_MFA,
|
|
||||||
} from "../../variables";
|
|
||||||
import { getUserAgentType } from "../../utils/posthog"; // TODO: move this
|
import { getUserAgentType } from "../../utils/posthog"; // TODO: move this
|
||||||
import {
|
import { getHttpsEnabled, getJwtMfaLifetime, getJwtMfaSecret } from "../../config";
|
||||||
getHttpsEnabled,
|
import { validateRequest } from "../../helpers/validation";
|
||||||
getJwtMfaLifetime,
|
import * as reqValidator from "../../validation/auth";
|
||||||
getJwtMfaSecret,
|
|
||||||
} from "../../config";
|
|
||||||
|
|
||||||
declare module "jsonwebtoken" {
|
declare module "jsonwebtoken" {
|
||||||
export interface UserIDJwtPayload extends jwt.JwtPayload {
|
export interface UserIDJwtPayload extends jwt.JwtPayload {
|
||||||
@@ -34,13 +29,10 @@ declare module "jsonwebtoken" {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const login1 = async (req: Request, res: Response) => {
|
export const login1 = async (req: Request, res: Response) => {
|
||||||
const {
|
const { email, clientPublicKey }: { email: string; clientPublicKey: string } = req.body;
|
||||||
email,
|
|
||||||
clientPublicKey,
|
|
||||||
}: { email: string; clientPublicKey: string } = req.body;
|
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email,
|
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");
|
||||||
@@ -49,25 +41,28 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
server.init(
|
server.init(
|
||||||
{
|
{
|
||||||
salt: user.salt,
|
salt: user.salt,
|
||||||
verifier: user.verifier,
|
verifier: user.verifier
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
// generate server-side public key
|
// generate server-side public key
|
||||||
const serverPublicKey = server.getPublicKey();
|
const serverPublicKey = server.getPublicKey();
|
||||||
|
|
||||||
await LoginSRPDetail.findOneAndReplace({ email: email }, {
|
await LoginSRPDetail.findOneAndReplace(
|
||||||
email: email,
|
{ email: email },
|
||||||
clientPublicKey: clientPublicKey,
|
{
|
||||||
serverBInt: bigintConversion.bigintToBuf(server.bInt),
|
email: email,
|
||||||
}, { upsert: true, returnNewDocument: false });
|
clientPublicKey: clientPublicKey,
|
||||||
|
serverBInt: bigintConversion.bigintToBuf(server.bInt)
|
||||||
|
},
|
||||||
|
{ upsert: true, returnNewDocument: false }
|
||||||
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serverPublicKey,
|
serverPublicKey,
|
||||||
salt: user.salt,
|
salt: user.salt
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,19 +73,22 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const login2 = async (req: Request, res: Response) => {
|
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 { email, clientProof } = req.body;
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email,
|
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");
|
||||||
|
|
||||||
const loginSRPDetail = await LoginSRPDetail.findOneAndDelete({ email: email })
|
const loginSRPDetail = await LoginSRPDetail.findOneAndDelete({ email: email });
|
||||||
|
|
||||||
if (!loginSRPDetail) {
|
if (!loginSRPDetail) {
|
||||||
return BadRequestError(Error("Failed to find login details for SRP"))
|
return BadRequestError(Error("Failed to find login details for SRP"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = new jsrp.server();
|
const server = new jsrp.server();
|
||||||
@@ -98,7 +96,7 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
{
|
{
|
||||||
salt: user.salt,
|
salt: user.salt,
|
||||||
verifier: user.verifier,
|
verifier: user.verifier,
|
||||||
b: loginSRPDetail.serverBInt,
|
b: loginSRPDetail.serverBInt
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
|
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
|
||||||
@@ -111,15 +109,15 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
// generate temporary MFA token
|
// generate temporary MFA token
|
||||||
const token = createToken({
|
const token = createToken({
|
||||||
payload: {
|
payload: {
|
||||||
userId: user._id.toString(),
|
userId: user._id.toString()
|
||||||
},
|
},
|
||||||
expiresIn: await getJwtMfaLifetime(),
|
expiresIn: await getJwtMfaLifetime(),
|
||||||
secret: await getJwtMfaSecret(),
|
secret: await getJwtMfaSecret()
|
||||||
});
|
});
|
||||||
|
|
||||||
const code = await TokenService.createToken({
|
const code = await TokenService.createToken({
|
||||||
type: TOKEN_EMAIL_MFA,
|
type: TOKEN_EMAIL_MFA,
|
||||||
email,
|
email
|
||||||
});
|
});
|
||||||
|
|
||||||
// send MFA code [code] to [email]
|
// send MFA code [code] to [email]
|
||||||
@@ -128,27 +126,27 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
subjectLine: "Infisical MFA code",
|
subjectLine: "Infisical MFA code",
|
||||||
recipients: [email],
|
recipients: [email],
|
||||||
substitutions: {
|
substitutions: {
|
||||||
code,
|
code
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
mfaEnabled: true,
|
mfaEnabled: true,
|
||||||
token,
|
token
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await checkUserDevice({
|
await checkUserDevice({
|
||||||
user,
|
user,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers["user-agent"] ?? "",
|
userAgent: req.headers["user-agent"] ?? ""
|
||||||
});
|
});
|
||||||
|
|
||||||
// issue tokens
|
// issue tokens
|
||||||
const tokens = await issueAuthTokens({
|
const tokens = await issueAuthTokens({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers["user-agent"] ?? "",
|
userAgent: req.headers["user-agent"] ?? ""
|
||||||
});
|
});
|
||||||
|
|
||||||
// store (refresh) token in httpOnly cookie
|
// store (refresh) token in httpOnly cookie
|
||||||
@@ -156,7 +154,7 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: "/",
|
path: "/",
|
||||||
sameSite: "strict",
|
sameSite: "strict",
|
||||||
secure: await getHttpsEnabled(),
|
secure: await getHttpsEnabled()
|
||||||
});
|
});
|
||||||
|
|
||||||
// case: user does not have MFA enabled
|
// case: user does not have MFA enabled
|
||||||
@@ -182,36 +180,33 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
publicKey: user.publicKey,
|
publicKey: user.publicKey,
|
||||||
encryptedPrivateKey: user.encryptedPrivateKey,
|
encryptedPrivateKey: user.encryptedPrivateKey,
|
||||||
iv: user.iv,
|
iv: user.iv,
|
||||||
tag: user.tag,
|
tag: user.tag
|
||||||
}
|
};
|
||||||
|
|
||||||
if (
|
if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) {
|
||||||
user?.protectedKey &&
|
|
||||||
user?.protectedKeyIV &&
|
|
||||||
user?.protectedKeyTag
|
|
||||||
) {
|
|
||||||
response.protectedKey = user.protectedKey;
|
response.protectedKey = user.protectedKey;
|
||||||
response.protectedKeyIV = user.protectedKeyIV
|
response.protectedKeyIV = user.protectedKeyIV;
|
||||||
response.protectedKeyTag = user.protectedKeyTag;
|
response.protectedKeyTag = user.protectedKeyTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
const loginAction = await EELogService.createAction({
|
const loginAction = await EELogService.createAction({
|
||||||
name: ACTION_LOGIN,
|
name: ACTION_LOGIN,
|
||||||
userId: user._id,
|
userId: user._id
|
||||||
});
|
});
|
||||||
|
|
||||||
loginAction && await EELogService.createLog({
|
loginAction &&
|
||||||
userId: user._id,
|
(await EELogService.createLog({
|
||||||
actions: [loginAction],
|
userId: user._id,
|
||||||
channel: getUserAgentType(req.headers["user-agent"]),
|
actions: [loginAction],
|
||||||
ipAddress: req.ip,
|
channel: getUserAgentType(req.headers["user-agent"]),
|
||||||
});
|
ipAddress: req.ip
|
||||||
|
}));
|
||||||
|
|
||||||
return res.status(200).send(response);
|
return res.status(200).send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: "Failed to authenticate. Try again?",
|
message: "Failed to authenticate. Try again?"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -223,11 +218,13 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const sendMfaToken = async (req: Request, res: Response) => {
|
export const sendMfaToken = async (req: Request, res: Response) => {
|
||||||
const { email } = req.body;
|
const {
|
||||||
|
body: { email }
|
||||||
|
} = await validateRequest(reqValidator.SendMfaTokenV2, req);
|
||||||
|
|
||||||
const code = await TokenService.createToken({
|
const code = await TokenService.createToken({
|
||||||
type: TOKEN_EMAIL_MFA,
|
type: TOKEN_EMAIL_MFA,
|
||||||
email,
|
email
|
||||||
});
|
});
|
||||||
|
|
||||||
// send MFA code [code] to [email]
|
// send MFA code [code] to [email]
|
||||||
@@ -236,14 +233,14 @@ export const sendMfaToken = async (req: Request, res: Response) => {
|
|||||||
subjectLine: "Infisical MFA code",
|
subjectLine: "Infisical MFA code",
|
||||||
recipients: [email],
|
recipients: [email],
|
||||||
substitutions: {
|
substitutions: {
|
||||||
code,
|
code
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: "Successfully sent new MFA code",
|
message: "Successfully sent new MFA code"
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify MFA token [mfaToken] and issue JWT and refresh tokens if the
|
* Verify MFA token [mfaToken] and issue JWT and refresh tokens if the
|
||||||
@@ -252,33 +249,37 @@ export const sendMfaToken = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const verifyMfaToken = async (req: Request, res: Response) => {
|
export const verifyMfaToken = async (req: Request, res: Response) => {
|
||||||
const { email, mfaToken } = req.body;
|
const {
|
||||||
|
body: { email, mfaToken }
|
||||||
|
} = await validateRequest(reqValidator.VerifyMfaTokenV2, req);
|
||||||
|
|
||||||
await TokenService.validateToken({
|
await TokenService.validateToken({
|
||||||
type: TOKEN_EMAIL_MFA,
|
type: TOKEN_EMAIL_MFA,
|
||||||
email,
|
email,
|
||||||
token: mfaToken,
|
token: mfaToken
|
||||||
});
|
});
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email,
|
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");
|
||||||
|
|
||||||
await LoginSRPDetail.deleteOne({ userId: user.id })
|
await LoginSRPDetail.deleteOne({ userId: user.id });
|
||||||
|
|
||||||
await checkUserDevice({
|
await checkUserDevice({
|
||||||
user,
|
user,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers["user-agent"] ?? "",
|
userAgent: req.headers["user-agent"] ?? ""
|
||||||
});
|
});
|
||||||
|
|
||||||
// issue tokens
|
// issue tokens
|
||||||
const tokens = await issueAuthTokens({
|
const tokens = await issueAuthTokens({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers["user-agent"] ?? "",
|
userAgent: req.headers["user-agent"] ?? ""
|
||||||
});
|
});
|
||||||
|
|
||||||
// store (refresh) token in httpOnly cookie
|
// store (refresh) token in httpOnly cookie
|
||||||
@@ -286,7 +287,7 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
|||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: "/",
|
path: "/",
|
||||||
sameSite: "strict",
|
sameSite: "strict",
|
||||||
secure: await getHttpsEnabled(),
|
secure: await getHttpsEnabled()
|
||||||
});
|
});
|
||||||
|
|
||||||
interface VerifyMfaTokenRes {
|
interface VerifyMfaTokenRes {
|
||||||
@@ -319,8 +320,8 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
|||||||
publicKey: user.publicKey as string,
|
publicKey: user.publicKey as string,
|
||||||
encryptedPrivateKey: user.encryptedPrivateKey as string,
|
encryptedPrivateKey: user.encryptedPrivateKey as string,
|
||||||
iv: user.iv as string,
|
iv: user.iv as string,
|
||||||
tag: user.tag as string,
|
tag: user.tag as string
|
||||||
}
|
};
|
||||||
|
|
||||||
if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) {
|
if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) {
|
||||||
resObj.protectedKey = user.protectedKey;
|
resObj.protectedKey = user.protectedKey;
|
||||||
@@ -330,15 +331,16 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const loginAction = await EELogService.createAction({
|
const loginAction = await EELogService.createAction({
|
||||||
name: ACTION_LOGIN,
|
name: ACTION_LOGIN,
|
||||||
userId: user._id,
|
userId: user._id
|
||||||
});
|
});
|
||||||
|
|
||||||
loginAction && await EELogService.createLog({
|
loginAction &&
|
||||||
userId: user._id,
|
(await EELogService.createLog({
|
||||||
actions: [loginAction],
|
userId: user._id,
|
||||||
channel: getUserAgentType(req.headers["user-agent"]),
|
actions: [loginAction],
|
||||||
ipAddress: req.realIP,
|
channel: getUserAgentType(req.headers["user-agent"]),
|
||||||
});
|
ipAddress: req.realIP
|
||||||
|
}));
|
||||||
|
|
||||||
return res.status(200).send(resObj);
|
return res.status(200).send(resObj);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -6,13 +6,21 @@ import {
|
|||||||
Secret,
|
Secret,
|
||||||
ServiceToken,
|
ServiceToken,
|
||||||
ServiceTokenData,
|
ServiceTokenData,
|
||||||
Workspace,
|
Workspace
|
||||||
} from "../../models";
|
} from "../../models";
|
||||||
import { EventType, SecretVersion } from "../../ee/models";
|
import { EventType, SecretVersion } from "../../ee/models";
|
||||||
import { EEAuditLogService, EELicenseService } from "../../ee/services";
|
import { EEAuditLogService, EELicenseService } from "../../ee/services";
|
||||||
import { BadRequestError, WorkspaceNotFoundError } from "../../utils/errors";
|
import { BadRequestError, WorkspaceNotFoundError } from "../../utils/errors";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables";
|
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/environments";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new workspace environment named [environmentName] under workspace with id
|
* Create new workspace environment named [environmentName] under workspace with id
|
||||||
@@ -20,13 +28,18 @@ import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variabl
|
|||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const createWorkspaceEnvironment = async (
|
export const createWorkspaceEnvironment = async (req: Request, res: Response) => {
|
||||||
req: Request,
|
const {
|
||||||
res: Response
|
params: { workspaceId },
|
||||||
) => {
|
body: { environmentName, environmentSlug }
|
||||||
|
} = await validateRequest(reqValidator.CreateWorkspaceEnvironmentV2, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionSub.Environments
|
||||||
|
);
|
||||||
|
|
||||||
const { workspaceId } = req.params;
|
|
||||||
const { environmentName, environmentSlug } = req.body;
|
|
||||||
const workspace = await Workspace.findById(workspaceId).exec();
|
const workspace = await Workspace.findById(workspaceId).exec();
|
||||||
|
|
||||||
if (!workspace) throw WorkspaceNotFoundError();
|
if (!workspace) throw WorkspaceNotFoundError();
|
||||||
@@ -39,7 +52,8 @@ export const createWorkspaceEnvironment = async (
|
|||||||
// case: number of environments used exceeds the number of environments allowed
|
// case: number of environments used exceeds the number of environments allowed
|
||||||
|
|
||||||
return res.status(400).send({
|
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."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,7 +69,7 @@ export const createWorkspaceEnvironment = async (
|
|||||||
|
|
||||||
workspace?.environments.push({
|
workspace?.environments.push({
|
||||||
name: environmentName,
|
name: environmentName,
|
||||||
slug: environmentSlug.toLowerCase(),
|
slug: environmentSlug.toLowerCase()
|
||||||
});
|
});
|
||||||
await workspace.save();
|
await workspace.save();
|
||||||
|
|
||||||
@@ -80,8 +94,8 @@ export const createWorkspaceEnvironment = async (
|
|||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment: {
|
environment: {
|
||||||
name: environmentName,
|
name: environmentName,
|
||||||
slug: environmentSlug,
|
slug: environmentSlug
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -129,12 +143,18 @@ export const reorderWorkspaceEnvironments = async (
|
|||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const renameWorkspaceEnvironment = async (
|
export const renameWorkspaceEnvironment = async (req: Request, res: Response) => {
|
||||||
req: Request,
|
const {
|
||||||
res: Response
|
params: { workspaceId },
|
||||||
) => {
|
body: { environmentName, environmentSlug, oldEnvironmentSlug }
|
||||||
const { workspaceId } = req.params;
|
} = await validateRequest(reqValidator.UpdateWorkspaceEnvironmentV2, req);
|
||||||
const { environmentName, environmentSlug, oldEnvironmentSlug } = req.body;
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionSub.Environments
|
||||||
|
);
|
||||||
|
|
||||||
// user should pass both new slug and env name
|
// user should pass both new slug and env name
|
||||||
if (!environmentSlug || !environmentName) {
|
if (!environmentSlug || !environmentName) {
|
||||||
throw new Error("Invalid environment given.");
|
throw new Error("Invalid environment given.");
|
||||||
@@ -148,16 +168,13 @@ export const renameWorkspaceEnvironment = async (
|
|||||||
|
|
||||||
const isEnvExist = workspace.environments.some(
|
const isEnvExist = workspace.environments.some(
|
||||||
({ name, slug }) =>
|
({ name, slug }) =>
|
||||||
slug !== oldEnvironmentSlug &&
|
slug !== oldEnvironmentSlug && (name === environmentName || slug === environmentSlug)
|
||||||
(name === environmentName || slug === environmentSlug)
|
|
||||||
);
|
);
|
||||||
if (isEnvExist) {
|
if (isEnvExist) {
|
||||||
throw new Error("Invalid environment given");
|
throw new Error("Invalid environment given");
|
||||||
}
|
}
|
||||||
|
|
||||||
const envIndex = workspace?.environments.findIndex(
|
const envIndex = workspace?.environments.findIndex(({ slug }) => slug === oldEnvironmentSlug);
|
||||||
({ slug }) => slug === oldEnvironmentSlug
|
|
||||||
);
|
|
||||||
if (envIndex === -1) {
|
if (envIndex === -1) {
|
||||||
throw new Error("Invalid environment given");
|
throw new Error("Invalid environment given");
|
||||||
}
|
}
|
||||||
@@ -191,7 +208,7 @@ export const renameWorkspaceEnvironment = async (
|
|||||||
await Membership.updateMany(
|
await Membership.updateMany(
|
||||||
{
|
{
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
"deniedPermissions.environmentSlug": oldEnvironmentSlug,
|
"deniedPermissions.environmentSlug": oldEnvironmentSlug
|
||||||
},
|
},
|
||||||
{ $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } },
|
{ $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } },
|
||||||
{ arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] }
|
{ arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] }
|
||||||
@@ -218,8 +235,8 @@ export const renameWorkspaceEnvironment = async (
|
|||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment: {
|
environment: {
|
||||||
name: environmentName,
|
name: environmentName,
|
||||||
slug: environmentSlug,
|
slug: environmentSlug
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -229,21 +246,25 @@ export const renameWorkspaceEnvironment = async (
|
|||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const deleteWorkspaceEnvironment = async (
|
export const deleteWorkspaceEnvironment = async (req: Request, res: Response) => {
|
||||||
req: Request,
|
const {
|
||||||
res: Response
|
params: { workspaceId },
|
||||||
) => {
|
body: { environmentSlug }
|
||||||
const { workspaceId } = req.params;
|
} = await validateRequest(reqValidator.DeleteWorkspaceEnvironmentV2, req);
|
||||||
const { environmentSlug } = req.body;
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
ProjectPermissionSub.Environments
|
||||||
|
);
|
||||||
|
|
||||||
// atomic update the env to avoid conflict
|
// atomic update the env to avoid conflict
|
||||||
const workspace = await Workspace.findById(workspaceId).exec();
|
const workspace = await Workspace.findById(workspaceId).exec();
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
throw new Error("Failed to create workspace environment");
|
throw new Error("Failed to create workspace environment");
|
||||||
}
|
}
|
||||||
|
|
||||||
const envIndex = workspace?.environments.findIndex(
|
const envIndex = workspace?.environments.findIndex(({ slug }) => slug === environmentSlug);
|
||||||
({ slug }) => slug === environmentSlug
|
|
||||||
);
|
|
||||||
if (envIndex === -1) {
|
if (envIndex === -1) {
|
||||||
throw new Error("Invalid environment given");
|
throw new Error("Invalid environment given");
|
||||||
}
|
}
|
||||||
@@ -256,11 +277,11 @@ export const deleteWorkspaceEnvironment = async (
|
|||||||
// clean up
|
// clean up
|
||||||
await Secret.deleteMany({
|
await Secret.deleteMany({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment: environmentSlug,
|
environment: environmentSlug
|
||||||
});
|
});
|
||||||
await SecretVersion.deleteMany({
|
await SecretVersion.deleteMany({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment: environmentSlug,
|
environment: environmentSlug
|
||||||
});
|
});
|
||||||
|
|
||||||
// await ServiceToken.deleteMany({
|
// await ServiceToken.deleteMany({
|
||||||
@@ -279,7 +300,7 @@ export const deleteWorkspaceEnvironment = async (
|
|||||||
|
|
||||||
await Integration.deleteMany({
|
await Integration.deleteMany({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment: environmentSlug,
|
environment: environmentSlug
|
||||||
});
|
});
|
||||||
await Membership.updateMany(
|
await Membership.updateMany(
|
||||||
{ workspace: workspaceId },
|
{ workspace: workspaceId },
|
||||||
@@ -305,46 +326,48 @@ export const deleteWorkspaceEnvironment = async (
|
|||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: "Successfully deleted environment",
|
message: "Successfully deleted environment",
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment: environmentSlug,
|
environment: environmentSlug
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO(akhilmhdh) after rbac this can be completely removed
|
||||||
|
export const getAllAccessibleEnvironmentsOfWorkspace = async (req: Request, res: Response) => {
|
||||||
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetAllAccessibileEnvironmentsOfWorkspaceV2, req);
|
||||||
|
|
||||||
export const getAllAccessibleEnvironmentsOfWorkspace = async (
|
const { membership: workspacesUserIsMemberOf } = await getUserProjectPermissions(
|
||||||
req: Request,
|
req.user._id,
|
||||||
res: Response
|
workspaceId
|
||||||
) => {
|
);
|
||||||
const { workspaceId } = req.params;
|
|
||||||
const workspacesUserIsMemberOf = await Membership.findOne({
|
|
||||||
workspace: workspaceId,
|
|
||||||
user: req.user,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!workspacesUserIsMemberOf) {
|
const accessibleEnvironments: any = [];
|
||||||
throw BadRequestError()
|
const deniedPermission = workspacesUserIsMemberOf.deniedPermissions;
|
||||||
}
|
|
||||||
|
|
||||||
const accessibleEnvironments: any = []
|
const relatedWorkspace = await Workspace.findById(workspaceId);
|
||||||
const deniedPermission = workspacesUserIsMemberOf.deniedPermissions
|
|
||||||
|
|
||||||
const relatedWorkspace = await Workspace.findById(workspaceId)
|
|
||||||
if (!relatedWorkspace) {
|
if (!relatedWorkspace) {
|
||||||
throw BadRequestError()
|
throw BadRequestError();
|
||||||
}
|
}
|
||||||
relatedWorkspace.environments.forEach(environment => {
|
relatedWorkspace.environments.forEach((environment) => {
|
||||||
const isReadBlocked = _.some(deniedPermission, { environmentSlug: environment.slug, ability: PERMISSION_READ_SECRETS })
|
const isReadBlocked = _.some(deniedPermission, {
|
||||||
const isWriteBlocked = _.some(deniedPermission, { environmentSlug: environment.slug, ability: PERMISSION_WRITE_SECRETS })
|
environmentSlug: environment.slug,
|
||||||
|
ability: PERMISSION_READ_SECRETS
|
||||||
|
});
|
||||||
|
const isWriteBlocked = _.some(deniedPermission, {
|
||||||
|
environmentSlug: environment.slug,
|
||||||
|
ability: PERMISSION_WRITE_SECRETS
|
||||||
|
});
|
||||||
if (isReadBlocked && isWriteBlocked) {
|
if (isReadBlocked && isWriteBlocked) {
|
||||||
return
|
return;
|
||||||
} else {
|
} else {
|
||||||
accessibleEnvironments.push({
|
accessibleEnvironments.push({
|
||||||
name: environment.name,
|
name: environment.name,
|
||||||
slug: environment.slug,
|
slug: environment.slug,
|
||||||
isWriteDenied: isWriteBlocked,
|
isWriteDenied: isWriteBlocked,
|
||||||
isReadDenied: isReadBlocked,
|
isReadDenied: isReadBlocked
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
res.json({ accessibleEnvironments })
|
res.json({ accessibleEnvironments });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
userHasWriteOnlyAbility
|
userHasWriteOnlyAbility
|
||||||
} from "../../ee/helpers/checkMembershipPermissions";
|
} from "../../ee/helpers/checkMembershipPermissions";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { BatchSecret, BatchSecretRequest } from "../../types/secret";
|
import { BatchSecret } from "../../types/secret";
|
||||||
import {
|
import {
|
||||||
getFolderByPath,
|
getFolderByPath,
|
||||||
getFolderIdFromServiceToken,
|
getFolderIdFromServiceToken,
|
||||||
@@ -35,6 +35,8 @@ import {
|
|||||||
import { isValidScope } from "../../helpers/secrets";
|
import { isValidScope } from "../../helpers/secrets";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { getAllImportedSecrets } from "../../services/SecretImportService";
|
import { getAllImportedSecrets } from "../../services/SecretImportService";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import { BatchSecretsV2, GetSecretsV2 } from "../../validation";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Peform a batch of any specified CUD secret operations
|
* Peform a batch of any specified CUD secret operations
|
||||||
@@ -46,22 +48,17 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
const channel = getUserAgentType(req.headers["user-agent"]);
|
const channel = getUserAgentType(req.headers["user-agent"]);
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
|
|
||||||
|
const validatedData = await validateRequest(BatchSecretsV2, req);
|
||||||
const {
|
const {
|
||||||
workspaceId,
|
body: { workspaceId, environment, requests }
|
||||||
environment,
|
} = validatedData;
|
||||||
requests
|
let {
|
||||||
}: {
|
body: { secretPath, folderId }
|
||||||
workspaceId: string;
|
} = validatedData;
|
||||||
environment: string;
|
|
||||||
requests: BatchSecretRequest[];
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
let secretPath = req.body.secretPath as string;
|
|
||||||
let folderId = req.body.folderId as string;
|
|
||||||
|
|
||||||
const createSecrets: BatchSecret[] = [];
|
const createSecrets: BatchSecret[] = [];
|
||||||
const updateSecrets: BatchSecret[] = [];
|
const updateSecrets: BatchSecret[] = [];
|
||||||
const deleteSecrets: { _id: Types.ObjectId, secretName: string; }[] = [];
|
const deleteSecrets: { _id: Types.ObjectId; secretName: string }[] = [];
|
||||||
const actions: IAction[] = [];
|
const actions: IAction[] = [];
|
||||||
|
|
||||||
// get secret blind index salt
|
// get secret blind index salt
|
||||||
@@ -72,7 +69,11 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
||||||
|
|
||||||
if (req.authData.authPayload instanceof ServiceTokenData) {
|
if (req.authData.authPayload instanceof ServiceTokenData) {
|
||||||
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, secretPath);
|
const isValidScopeAccess = isValidScope(
|
||||||
|
req.authData.authPayload,
|
||||||
|
environment,
|
||||||
|
secretPath || "/"
|
||||||
|
);
|
||||||
|
|
||||||
// in service token when not giving secretpath folderid must be root
|
// in service token when not giving secretpath folderid must be root
|
||||||
// this is to avoid giving folderid when service tokens are used
|
// this is to avoid giving folderid when service tokens are used
|
||||||
@@ -133,7 +134,10 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "DELETE":
|
case "DELETE":
|
||||||
deleteSecrets.push({ _id: new Types.ObjectId(request.secret._id), secretName: request.secret.secretName });
|
deleteSecrets.push({
|
||||||
|
_id: new Types.ObjectId(request.secret._id),
|
||||||
|
secretName: request.secret.secretName
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,18 +336,19 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
if (deleteSecrets.length > 0) {
|
if (deleteSecrets.length > 0) {
|
||||||
const deleteSecretIds: Types.ObjectId[] = deleteSecrets.map((s) => s._id);
|
const deleteSecretIds: Types.ObjectId[] = deleteSecrets.map((s) => s._id);
|
||||||
|
|
||||||
const deletedSecretsObj = (await Secret.find({
|
const deletedSecretsObj = (
|
||||||
_id: {
|
await Secret.find({
|
||||||
$in: deleteSecretIds
|
_id: {
|
||||||
}
|
$in: deleteSecretIds
|
||||||
}))
|
}
|
||||||
.reduce(
|
})
|
||||||
(obj: any, secret: ISecret) => ({
|
).reduce(
|
||||||
...obj,
|
(obj: any, secret: ISecret) => ({
|
||||||
[secret._id.toString()]: secret
|
...obj,
|
||||||
}),
|
[secret._id.toString()]: secret
|
||||||
{}
|
}),
|
||||||
);
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
await Secret.deleteMany({
|
await Secret.deleteMany({
|
||||||
_id: {
|
_id: {
|
||||||
@@ -781,10 +786,13 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { tagSlugs, secretPath, include_imports } = req.query;
|
const validatedData = await validateRequest(GetSecretsV2, req);
|
||||||
let { folderId } = req.query;
|
const {
|
||||||
const workspaceId = req.query.workspaceId as string;
|
query: { tagSlugs, secretPath, include_imports, workspaceId, environment }
|
||||||
const environment = req.query.environment as string;
|
} = validatedData;
|
||||||
|
let {
|
||||||
|
query: { folderId }
|
||||||
|
} = validatedData;
|
||||||
|
|
||||||
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
||||||
|
|
||||||
@@ -926,7 +934,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
// TODO(akhilmhdh) - secret-imp change this to org type
|
// TODO(akhilmhdh) - secret-imp change this to org type
|
||||||
let importedSecrets: any[] = [];
|
let importedSecrets: any[] = [];
|
||||||
if (include_imports === "true") {
|
if (include_imports) {
|
||||||
importedSecrets = await getAllImportedSecrets(workspaceId, environment, folderId as string);
|
importedSecrets = await getAllImportedSecrets(workspaceId, environment, folderId as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,15 @@ import { getSaltRounds } from "../../config";
|
|||||||
import { BadRequestError } from "../../utils/errors";
|
import { BadRequestError } from "../../utils/errors";
|
||||||
import { ActorType, EventType } from "../../ee/models";
|
import { ActorType, EventType } from "../../ee/models";
|
||||||
import { EEAuditLogService } from "../../ee/services";
|
import { EEAuditLogService } from "../../ee/services";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/serviceTokenData";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
import { Types } from "mongoose";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return service token data associated with service token on request
|
* Return service token data associated with service token on request
|
||||||
@@ -63,7 +72,14 @@ export const getServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
export const createServiceTokenData = async (req: Request, res: Response) => {
|
export const createServiceTokenData = async (req: Request, res: Response) => {
|
||||||
let serviceTokenData;
|
let serviceTokenData;
|
||||||
|
|
||||||
const { name, workspaceId, encryptedKey, iv, tag, expiresIn, permissions, scopes } = req.body;
|
const {
|
||||||
|
body: { workspaceId, permissions, tag, encryptedKey, scopes, name, expiresIn, iv }
|
||||||
|
} = await validateRequest(reqValidator.CreateServiceTokenV2, req);
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionSub.ServiceTokens
|
||||||
|
);
|
||||||
|
|
||||||
const secret = crypto.randomBytes(16).toString("hex");
|
const secret = crypto.randomBytes(16).toString("hex");
|
||||||
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||||
@@ -111,7 +127,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
workspaceId
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -128,13 +144,28 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
||||||
const { serviceTokenDataId } = req.params;
|
const {
|
||||||
|
params: { serviceTokenDataId }
|
||||||
|
} = await validateRequest(reqValidator.DeleteServiceTokenV2, req);
|
||||||
|
|
||||||
const serviceTokenData = await ServiceTokenData.findByIdAndDelete(serviceTokenDataId);
|
let serviceTokenData = await ServiceTokenData.findById(serviceTokenDataId);
|
||||||
|
if (!serviceTokenData) throw BadRequestError({ message: "Service token not found" });
|
||||||
|
|
||||||
if (!serviceTokenData) return res.status(200).send({
|
const { permission } = await getUserProjectPermissions(
|
||||||
message: "Failed to delete service token"
|
req.user._id,
|
||||||
});
|
serviceTokenData.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
ProjectPermissionSub.ServiceTokens
|
||||||
|
);
|
||||||
|
|
||||||
|
serviceTokenData = await ServiceTokenData.findByIdAndDelete(serviceTokenDataId);
|
||||||
|
|
||||||
|
if (!serviceTokenData)
|
||||||
|
return res.status(200).send({
|
||||||
|
message: "Failed to delete service token"
|
||||||
|
});
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
req.authData,
|
req.authData,
|
||||||
|
|||||||
@@ -1,19 +1,34 @@
|
|||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { Membership, Secret, Tag } from "../../models";
|
import { Secret, Tag } from "../../models";
|
||||||
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
import { BadRequestError } from "../../utils/errors";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
|
import * as reqValidator from "../../validation/tags";
|
||||||
|
|
||||||
export const createWorkspaceTag = async (req: Request, res: Response) => {
|
export const createWorkspaceTag = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
const { name, slug, tagColor } = req.body;
|
body: { name, slug },
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.CreateWorkspaceTagsV2, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionSub.Tags
|
||||||
|
);
|
||||||
|
|
||||||
const tagToCreate = {
|
const tagToCreate = {
|
||||||
name,
|
name,
|
||||||
tagColor,
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
slug,
|
||||||
slug,
|
user: new Types.ObjectId(req.user._id)
|
||||||
user: new Types.ObjectId(req.user._id),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
const createdTag = await new Tag(tagToCreate).save();
|
const createdTag = await new Tag(tagToCreate).save();
|
||||||
|
|
||||||
@@ -21,22 +36,23 @@ export const createWorkspaceTag = async (req: Request, res: Response) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const deleteWorkspaceTag = async (req: Request, res: Response) => {
|
export const deleteWorkspaceTag = async (req: Request, res: Response) => {
|
||||||
const { tagId } = req.params;
|
const {
|
||||||
|
params: { tagId }
|
||||||
|
} = await validateRequest(reqValidator.DeleteWorkspaceTagsV2, req);
|
||||||
|
|
||||||
const tagFromDB = await Tag.findById(tagId);
|
const tagFromDB = await Tag.findById(tagId);
|
||||||
if (!tagFromDB) {
|
if (!tagFromDB) {
|
||||||
throw BadRequestError();
|
throw BadRequestError();
|
||||||
}
|
}
|
||||||
|
|
||||||
// can only delete if the request user is one that belongs to the same workspace as the tag
|
const { permission } = await getUserProjectPermissions(
|
||||||
const membership = await Membership.findOne({
|
req.user._id,
|
||||||
user: req.user,
|
tagFromDB.workspace.toString()
|
||||||
workspace: tagFromDB.workspace
|
);
|
||||||
});
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
if (!membership) {
|
ProjectPermissionSub.Tags
|
||||||
UnauthorizedRequestError({ message: "Failed to validate membership" });
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const result = await Tag.findByIdAndDelete(tagId);
|
const result = await Tag.findByIdAndDelete(tagId);
|
||||||
|
|
||||||
@@ -47,7 +63,14 @@ export const deleteWorkspaceTag = async (req: Request, res: Response) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getWorkspaceTags = async (req: Request, res: Response) => {
|
export const getWorkspaceTags = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetWorkspaceTagsV2, req);
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Tags
|
||||||
|
);
|
||||||
|
|
||||||
const workspaceTags = await Tag.find({
|
const workspaceTags = await Tag.find({
|
||||||
workspace: new Types.ObjectId(workspaceId)
|
workspace: new Types.ObjectId(workspaceId)
|
||||||
|
|||||||
@@ -2,14 +2,10 @@ import { Request, Response } from "express";
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
import {
|
import { APIKeyData, AuthMethod, MembershipOrg, TokenVersion, User } from "../../models";
|
||||||
APIKeyData,
|
|
||||||
AuthMethod,
|
|
||||||
MembershipOrg,
|
|
||||||
TokenVersion,
|
|
||||||
User
|
|
||||||
} from "../../models";
|
|
||||||
import { getSaltRounds } from "../../config";
|
import { getSaltRounds } from "../../config";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current user.
|
* Return the current user.
|
||||||
@@ -18,7 +14,7 @@ import { getSaltRounds } from "../../config";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getMe = async (req: Request, res: Response) => {
|
export const getMe = async (req: Request, res: Response) => {
|
||||||
/*
|
/*
|
||||||
#swagger.summary = "Retrieve the current user on the request"
|
#swagger.summary = "Retrieve the current user on the request"
|
||||||
#swagger.description = "Retrieve the current user on the request"
|
#swagger.description = "Retrieve the current user on the request"
|
||||||
|
|
||||||
@@ -43,14 +39,14 @@ export const getMe = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
const user = await User
|
const user = await User.findById(req.user._id).select(
|
||||||
.findById(req.user._id)
|
"+salt +publicKey +encryptedPrivateKey +iv +tag +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag"
|
||||||
.select("+salt +publicKey +encryptedPrivateKey +iv +tag +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag");
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
user,
|
user
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the current user's MFA-enabled status [isMfaEnabled].
|
* Update the current user's MFA-enabled status [isMfaEnabled].
|
||||||
@@ -61,25 +57,28 @@ export const getMe = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const updateMyMfaEnabled = async (req: Request, res: Response) => {
|
export const updateMyMfaEnabled = async (req: Request, res: Response) => {
|
||||||
const { isMfaEnabled }: { isMfaEnabled: boolean } = req.body;
|
const {
|
||||||
req.user.isMfaEnabled = isMfaEnabled;
|
body: { isMfaEnabled }
|
||||||
|
} = await validateRequest(reqValidator.UpdateMyMfaEnabledV2, req);
|
||||||
|
|
||||||
if (isMfaEnabled) {
|
req.user.isMfaEnabled = isMfaEnabled;
|
||||||
// TODO: adapt this route/controller
|
|
||||||
// to work for different forms of MFA
|
|
||||||
req.user.mfaMethods = ["email"];
|
|
||||||
} else {
|
|
||||||
req.user.mfaMethods = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
await req.user.save();
|
if (isMfaEnabled) {
|
||||||
|
// TODO: adapt this route/controller
|
||||||
|
// to work for different forms of MFA
|
||||||
|
req.user.mfaMethods = ["email"];
|
||||||
|
} else {
|
||||||
|
req.user.mfaMethods = [];
|
||||||
|
}
|
||||||
|
|
||||||
const user = req.user;
|
await req.user.save();
|
||||||
|
|
||||||
return res.status(200).send({
|
const user = req.user;
|
||||||
user,
|
|
||||||
});
|
return res.status(200).send({
|
||||||
}
|
user
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update name of the current user to [firstName, lastName].
|
* Update name of the current user to [firstName, lastName].
|
||||||
@@ -88,29 +87,25 @@ export const updateMyMfaEnabled = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const updateName = async (req: Request, res: Response) => {
|
export const updateName = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
firstName,
|
body: { lastName, firstName }
|
||||||
lastName
|
} = await validateRequest(reqValidator.UpdateNameV2, req);
|
||||||
}: {
|
|
||||||
firstName: string;
|
|
||||||
lastName: string;
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
const user = await User.findByIdAndUpdate(
|
const user = await User.findByIdAndUpdate(
|
||||||
req.user._id.toString(),
|
req.user._id.toString(),
|
||||||
{
|
{
|
||||||
firstName,
|
firstName,
|
||||||
lastName: lastName ?? ""
|
lastName: lastName ?? ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new: true
|
new: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
user,
|
user
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update auth method of the current user to [authMethods]
|
* Update auth method of the current user to [authMethods]
|
||||||
@@ -118,41 +113,35 @@ export const updateName = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const updateAuthMethods = async (req: Request, res: Response) => {
|
export const updateAuthMethods = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
authMethods
|
body: { authMethods }
|
||||||
} = req.body;
|
} = await validateRequest(reqValidator.UpdateAuthMethodsV2, req);
|
||||||
|
|
||||||
const hasSamlEnabled = req.user.authMethods
|
const hasSamlEnabled = req.user.authMethods.some((authMethod: AuthMethod) =>
|
||||||
.some(
|
[AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes(authMethod)
|
||||||
(authMethod: AuthMethod) => [
|
);
|
||||||
AuthMethod.OKTA_SAML,
|
|
||||||
AuthMethod.AZURE_SAML,
|
|
||||||
AuthMethod.JUMPCLOUD_SAML
|
|
||||||
].includes(authMethod)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (hasSamlEnabled) {
|
if (hasSamlEnabled) {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: "Failed to update user authentication method because SAML SSO is enforced"
|
message: "Failed to update user authentication method because SAML SSO is enforced"
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await User.findByIdAndUpdate(
|
|
||||||
req.user._id.toString(),
|
|
||||||
{
|
|
||||||
authMethods
|
|
||||||
},
|
|
||||||
{
|
|
||||||
new: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.status(200).send({
|
|
||||||
user
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const user = await User.findByIdAndUpdate(
|
||||||
|
req.user._id.toString(),
|
||||||
|
{
|
||||||
|
authMethods
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
user
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return organizations that the current user is part of.
|
* Return organizations that the current user is part of.
|
||||||
@@ -160,7 +149,7 @@ export const updateName = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getMyOrganizations = async (req: Request, res: Response) => {
|
export const getMyOrganizations = async (req: Request, res: Response) => {
|
||||||
/*
|
/*
|
||||||
#swagger.summary = 'Return organizations that current user is part of'
|
#swagger.summary = 'Return organizations that current user is part of'
|
||||||
#swagger.description = 'Return organizations that current user is part of'
|
#swagger.description = 'Return organizations that current user is part of'
|
||||||
|
|
||||||
@@ -189,14 +178,14 @@ export const getMyOrganizations = async (req: Request, res: Response) => {
|
|||||||
*/
|
*/
|
||||||
const organizations = (
|
const organizations = (
|
||||||
await MembershipOrg.find({
|
await MembershipOrg.find({
|
||||||
user: req.user._id,
|
user: req.user._id
|
||||||
}).populate("organization")
|
}).populate("organization")
|
||||||
).map((m) => m.organization);
|
).map((m) => m.organization);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
organizations,
|
organizations
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return API keys belonging to current user.
|
* Return API keys belonging to current user.
|
||||||
@@ -205,12 +194,12 @@ export const getMyOrganizations = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getMyAPIKeys = async (req: Request, res: Response) => {
|
export const getMyAPIKeys = async (req: Request, res: Response) => {
|
||||||
const apiKeyData = await APIKeyData.find({
|
const apiKeyData = await APIKeyData.find({
|
||||||
user: req.user._id,
|
user: req.user._id
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send(apiKeyData);
|
return res.status(200).send(apiKeyData);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new API key for current user.
|
* Create new API key for current user.
|
||||||
@@ -219,34 +208,36 @@ export const getMyAPIKeys = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const createAPIKey = async (req: Request, res: Response) => {
|
export const createAPIKey = async (req: Request, res: Response) => {
|
||||||
const { name, expiresIn } = req.body;
|
const {
|
||||||
|
body: { name, expiresIn }
|
||||||
|
} = await validateRequest(reqValidator.CreateApiKeyV2, req);
|
||||||
|
|
||||||
const secret = crypto.randomBytes(16).toString("hex");
|
const secret = crypto.randomBytes(16).toString("hex");
|
||||||
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||||
|
|
||||||
const expiresAt = new Date();
|
const expiresAt = new Date();
|
||||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||||
|
|
||||||
let apiKeyData = await new APIKeyData({
|
let apiKeyData = await new APIKeyData({
|
||||||
name,
|
name,
|
||||||
lastUsed: new Date(),
|
lastUsed: new Date(),
|
||||||
expiresAt,
|
expiresAt,
|
||||||
user: req.user._id,
|
user: req.user._id,
|
||||||
secretHash,
|
secretHash
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
// return api key data without sensitive data
|
// return api key data without sensitive data
|
||||||
apiKeyData = (await APIKeyData.findById(apiKeyData._id)) as any;
|
apiKeyData = (await APIKeyData.findById(apiKeyData._id)) as any;
|
||||||
|
|
||||||
if (!apiKeyData) throw new Error("Failed to find API key data");
|
if (!apiKeyData) throw new Error("Failed to find API key data");
|
||||||
|
|
||||||
const apiKey = `ak.${apiKeyData._id.toString()}.${secret}`;
|
const apiKey = `ak.${apiKeyData._id.toString()}.${secret}`;
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
apiKey,
|
apiKey,
|
||||||
apiKeyData,
|
apiKeyData
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete API key with id [apiKeyDataId] belonging to current user
|
* Delete API key with id [apiKeyDataId] belonging to current user
|
||||||
@@ -254,17 +245,19 @@ export const createAPIKey = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const deleteAPIKey = async (req: Request, res: Response) => {
|
export const deleteAPIKey = async (req: Request, res: Response) => {
|
||||||
const { apiKeyDataId } = req.params;
|
const {
|
||||||
|
params: { apiKeyDataId }
|
||||||
|
} = await validateRequest(reqValidator.DeleteApiKeyV2, req);
|
||||||
|
|
||||||
const apiKeyData = await APIKeyData.findOneAndDelete({
|
const apiKeyData = await APIKeyData.findOneAndDelete({
|
||||||
_id: new Types.ObjectId(apiKeyDataId),
|
_id: new Types.ObjectId(apiKeyDataId),
|
||||||
user: req.user._id
|
user: req.user._id
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
apiKeyData
|
apiKeyData
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return active sessions (TokenVersion) belonging to user
|
* Return active sessions (TokenVersion) belonging to user
|
||||||
@@ -273,12 +266,12 @@ export const deleteAPIKey = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getMySessions = async (req: Request, res: Response) => {
|
export const getMySessions = async (req: Request, res: Response) => {
|
||||||
const tokenVersions = await TokenVersion.find({
|
const tokenVersions = await TokenVersion.find({
|
||||||
user: req.user._id
|
user: req.user._id
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send(tokenVersions);
|
return res.status(200).send(tokenVersions);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Revoke all active sessions belong to user
|
* Revoke all active sessions belong to user
|
||||||
@@ -287,16 +280,19 @@ export const getMySessions = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const deleteMySessions = async (req: Request, res: Response) => {
|
export const deleteMySessions = async (req: Request, res: Response) => {
|
||||||
await TokenVersion.updateMany({
|
await TokenVersion.updateMany(
|
||||||
user: req.user._id,
|
{
|
||||||
}, {
|
user: req.user._id
|
||||||
$inc: {
|
},
|
||||||
refreshVersion: 1,
|
{
|
||||||
accessVersion: 1,
|
$inc: {
|
||||||
},
|
refreshVersion: 1,
|
||||||
});
|
accessVersion: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: "Successfully revoked all sessions"
|
message: "Successfully revoked all sessions"
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -11,6 +11,14 @@ import { EventService, TelemetryService } from "../../services";
|
|||||||
import { eventPushSecrets } from "../../events";
|
import { eventPushSecrets } from "../../events";
|
||||||
import { EEAuditLogService } from "../../ee/services";
|
import { EEAuditLogService } from "../../ee/services";
|
||||||
import { EventType } from "../../ee/models";
|
import { EventType } from "../../ee/models";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
interface V2PushSecret {
|
interface V2PushSecret {
|
||||||
type: string; // personal or shared
|
type: string; // personal or shared
|
||||||
@@ -181,7 +189,9 @@ export const getWorkspaceKey = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetWorkspaceKeyV2, req);
|
||||||
|
|
||||||
const key = await Key.findOne({
|
const key = await Key.findOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
@@ -258,7 +268,15 @@ export const getWorkspaceMemberships = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetWorkspaceMembershipsV2, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.Member
|
||||||
|
);
|
||||||
|
|
||||||
const memberships = await Membership.find({
|
const memberships = await Membership.find({
|
||||||
workspace: workspaceId
|
workspace: workspaceId
|
||||||
@@ -329,8 +347,16 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) =>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
const { membershipId } = req.params;
|
const {
|
||||||
const { role } = req.body;
|
params: { workspaceId, membershipId },
|
||||||
|
body: { role }
|
||||||
|
} = await validateRequest(reqValidator.UpdateWorkspaceMembershipsV2, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionSub.Member
|
||||||
|
);
|
||||||
|
|
||||||
const membership = await Membership.findByIdAndUpdate(
|
const membership = await Membership.findByIdAndUpdate(
|
||||||
membershipId,
|
membershipId,
|
||||||
@@ -390,7 +416,15 @@ export const deleteWorkspaceMembership = async (req: Request, res: Response) =>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
const { membershipId } = req.params;
|
const {
|
||||||
|
params: { workspaceId, membershipId }
|
||||||
|
} = await validateRequest(reqValidator.DeleteWorkspaceMembershipsV2, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
ProjectPermissionSub.Member
|
||||||
|
);
|
||||||
|
|
||||||
const membership = await Membership.findByIdAndDelete(membershipId);
|
const membership = await Membership.findByIdAndDelete(membershipId);
|
||||||
|
|
||||||
@@ -413,8 +447,16 @@ export const deleteWorkspaceMembership = async (req: Request, res: Response) =>
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
const { autoCapitalization } = req.body;
|
params: { workspaceId },
|
||||||
|
body: { autoCapitalization }
|
||||||
|
} = await validateRequest(reqValidator.ToggleAutoCapitalizationV2, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionSub.Settings
|
||||||
|
);
|
||||||
|
|
||||||
const workspace = await Workspace.findOneAndUpdate(
|
const workspace = await Workspace.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,25 +10,20 @@ import { sendMail } from "../../helpers/nodemailer";
|
|||||||
import { TokenService } from "../../services";
|
import { TokenService } from "../../services";
|
||||||
import { EELogService } from "../../ee/services";
|
import { EELogService } from "../../ee/services";
|
||||||
import { BadRequestError, InternalServerError } from "../../utils/errors";
|
import { BadRequestError, InternalServerError } from "../../utils/errors";
|
||||||
import {
|
import { ACTION_LOGIN, TOKEN_EMAIL_MFA } from "../../variables";
|
||||||
ACTION_LOGIN,
|
|
||||||
TOKEN_EMAIL_MFA,
|
|
||||||
} from "../../variables";
|
|
||||||
import { getUserAgentType } from "../../utils/posthog"; // TODO: move this
|
import { getUserAgentType } from "../../utils/posthog"; // TODO: move this
|
||||||
import {
|
import { getHttpsEnabled, getJwtMfaLifetime, getJwtMfaSecret } from "../../config";
|
||||||
getHttpsEnabled,
|
|
||||||
getJwtMfaLifetime,
|
|
||||||
getJwtMfaSecret,
|
|
||||||
} from "../../config";
|
|
||||||
import { AuthMethod } from "../../models/user";
|
import { AuthMethod } from "../../models/user";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/auth";
|
||||||
|
|
||||||
declare module "jsonwebtoken" {
|
declare module "jsonwebtoken" {
|
||||||
export interface ProviderAuthJwtPayload extends jwt.JwtPayload {
|
export interface ProviderAuthJwtPayload extends jwt.JwtPayload {
|
||||||
userId: string;
|
userId: string;
|
||||||
email: string;
|
email: string;
|
||||||
authProvider: AuthMethod;
|
authProvider: AuthMethod;
|
||||||
isUserCompleted: boolean,
|
isUserCompleted: boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,53 +33,51 @@ declare module "jsonwebtoken" {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const login1 = async (req: Request, res: Response) => {
|
export const login1 = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
email,
|
body: { email, clientPublicKey, providerAuthToken }
|
||||||
providerAuthToken,
|
} = await validateRequest(reqValidator.Login1V3, req);
|
||||||
clientPublicKey,
|
|
||||||
}: {
|
|
||||||
email: string;
|
|
||||||
clientPublicKey: string,
|
|
||||||
providerAuthToken?: string;
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email,
|
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.authMethods.includes(AuthMethod.EMAIL)) {
|
if (!user.authMethods.includes(AuthMethod.EMAIL)) {
|
||||||
await validateProviderAuthToken({
|
await validateProviderAuthToken({
|
||||||
email,
|
email,
|
||||||
providerAuthToken,
|
providerAuthToken
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = new jsrp.server();
|
const server = new jsrp.server();
|
||||||
server.init(
|
server.init(
|
||||||
|
{
|
||||||
|
salt: user.salt,
|
||||||
|
verifier: user.verifier
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
// generate server-side public key
|
||||||
|
const serverPublicKey = server.getPublicKey();
|
||||||
|
await LoginSRPDetail.findOneAndReplace(
|
||||||
{
|
{
|
||||||
salt: user.salt,
|
email: email
|
||||||
verifier: user.verifier,
|
|
||||||
},
|
},
|
||||||
async () => {
|
{
|
||||||
// generate server-side public key
|
email,
|
||||||
const serverPublicKey = server.getPublicKey();
|
userId: user.id,
|
||||||
await LoginSRPDetail.findOneAndReplace({
|
clientPublicKey: clientPublicKey,
|
||||||
email: email,
|
serverBInt: bigintConversion.bigintToBuf(server.bInt)
|
||||||
}, {
|
},
|
||||||
email,
|
{ upsert: true, returnNewDocument: false }
|
||||||
userId: user.id,
|
);
|
||||||
clientPublicKey: clientPublicKey,
|
|
||||||
serverBInt: bigintConversion.bigintToBuf(server.bInt),
|
|
||||||
}, { upsert: true, returnNewDocument: false });
|
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serverPublicKey,
|
serverPublicKey,
|
||||||
salt: user.salt,
|
salt: user.salt
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,150 +88,151 @@ export const login1 = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const login2 = async (req: Request, res: Response) => {
|
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, providerAuthToken } = req.body;
|
const {
|
||||||
|
body: { email, providerAuthToken, clientProof }
|
||||||
|
} = await validateRequest(reqValidator.Login2V3, req);
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
email,
|
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.authMethods.includes(AuthMethod.EMAIL)) {
|
if (!user.authMethods.includes(AuthMethod.EMAIL)) {
|
||||||
await validateProviderAuthToken({
|
await validateProviderAuthToken({
|
||||||
email,
|
email,
|
||||||
providerAuthToken,
|
providerAuthToken
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const loginSRPDetail = await LoginSRPDetail.findOneAndDelete({ email: email })
|
const loginSRPDetail = await LoginSRPDetail.findOneAndDelete({ email: email });
|
||||||
|
|
||||||
if (!loginSRPDetail) {
|
if (!loginSRPDetail) {
|
||||||
return BadRequestError(Error("Failed to find login details for SRP"))
|
return BadRequestError(Error("Failed to find login details for SRP"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = new jsrp.server();
|
const server = new jsrp.server();
|
||||||
server.init(
|
server.init(
|
||||||
{
|
{
|
||||||
salt: user.salt,
|
salt: user.salt,
|
||||||
verifier: user.verifier,
|
verifier: user.verifier,
|
||||||
b: loginSRPDetail.serverBInt,
|
b: loginSRPDetail.serverBInt
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
|
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
|
||||||
|
|
||||||
// compare server and client shared keys
|
// compare server and client shared keys
|
||||||
if (server.checkClientProof(clientProof)) {
|
if (server.checkClientProof(clientProof)) {
|
||||||
|
if (user.isMfaEnabled) {
|
||||||
|
// case: user has MFA enabled
|
||||||
|
|
||||||
if (user.isMfaEnabled) {
|
// generate temporary MFA token
|
||||||
// case: user has MFA enabled
|
const token = createToken({
|
||||||
|
payload: {
|
||||||
|
userId: user._id.toString()
|
||||||
|
},
|
||||||
|
expiresIn: await getJwtMfaLifetime(),
|
||||||
|
secret: await getJwtMfaSecret()
|
||||||
|
});
|
||||||
|
|
||||||
// generate temporary MFA token
|
const code = await TokenService.createToken({
|
||||||
const token = createToken({
|
type: TOKEN_EMAIL_MFA,
|
||||||
payload: {
|
email
|
||||||
userId: user._id.toString(),
|
});
|
||||||
},
|
|
||||||
expiresIn: await getJwtMfaLifetime(),
|
|
||||||
secret: await getJwtMfaSecret(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const code = await TokenService.createToken({
|
// send MFA code [code] to [email]
|
||||||
type: TOKEN_EMAIL_MFA,
|
await sendMail({
|
||||||
email,
|
template: "emailMfa.handlebars",
|
||||||
});
|
subjectLine: "Infisical MFA code",
|
||||||
|
recipients: [user.email],
|
||||||
// send MFA code [code] to [email]
|
substitutions: {
|
||||||
await sendMail({
|
code
|
||||||
template: "emailMfa.handlebars",
|
|
||||||
subjectLine: "Infisical MFA code",
|
|
||||||
recipients: [user.email],
|
|
||||||
substitutions: {
|
|
||||||
code,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(200).send({
|
|
||||||
mfaEnabled: true,
|
|
||||||
token,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await checkUserDevice({
|
|
||||||
user,
|
|
||||||
ip: req.realIP,
|
|
||||||
userAgent: req.headers["user-agent"] ?? "",
|
|
||||||
});
|
|
||||||
|
|
||||||
// issue tokens
|
|
||||||
const tokens = await issueAuthTokens({
|
|
||||||
userId: user._id,
|
|
||||||
ip: req.realIP,
|
|
||||||
userAgent: req.headers["user-agent"] ?? "",
|
|
||||||
});
|
|
||||||
|
|
||||||
// store (refresh) token in httpOnly cookie
|
|
||||||
res.cookie("jid", tokens.refreshToken, {
|
|
||||||
httpOnly: true,
|
|
||||||
path: "/",
|
|
||||||
sameSite: "strict",
|
|
||||||
secure: await getHttpsEnabled(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// case: user does not have MFA enablgged
|
|
||||||
// return (access) token in response
|
|
||||||
|
|
||||||
interface ResponseData {
|
|
||||||
mfaEnabled: boolean;
|
|
||||||
encryptionVersion: any;
|
|
||||||
protectedKey?: string;
|
|
||||||
protectedKeyIV?: string;
|
|
||||||
protectedKeyTag?: string;
|
|
||||||
token: string;
|
|
||||||
publicKey?: string;
|
|
||||||
encryptedPrivateKey?: string;
|
|
||||||
iv?: string;
|
|
||||||
tag?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response: ResponseData = {
|
|
||||||
mfaEnabled: false,
|
|
||||||
encryptionVersion: user.encryptionVersion,
|
|
||||||
token: tokens.token,
|
|
||||||
publicKey: user.publicKey,
|
|
||||||
encryptedPrivateKey: user.encryptedPrivateKey,
|
|
||||||
iv: user.iv,
|
|
||||||
tag: user.tag,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
user?.protectedKey &&
|
|
||||||
user?.protectedKeyIV &&
|
|
||||||
user?.protectedKeyTag
|
|
||||||
) {
|
|
||||||
response.protectedKey = user.protectedKey;
|
|
||||||
response.protectedKeyIV = user.protectedKeyIV
|
|
||||||
response.protectedKeyTag = user.protectedKeyTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
const loginAction = await EELogService.createAction({
|
|
||||||
name: ACTION_LOGIN,
|
|
||||||
userId: user._id,
|
|
||||||
});
|
|
||||||
|
|
||||||
loginAction && await EELogService.createLog({
|
|
||||||
userId: user._id,
|
|
||||||
actions: [loginAction],
|
|
||||||
channel: getUserAgentType(req.headers["user-agent"]),
|
|
||||||
ipAddress: req.realIP,
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(200).send(response);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return res.status(400).send({
|
return res.status(200).send({
|
||||||
message: "Failed to authenticate. Try again?",
|
mfaEnabled: true,
|
||||||
});
|
token
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
await checkUserDevice({
|
||||||
|
user,
|
||||||
|
ip: req.realIP,
|
||||||
|
userAgent: req.headers["user-agent"] ?? ""
|
||||||
|
});
|
||||||
|
|
||||||
|
// issue tokens
|
||||||
|
const tokens = await issueAuthTokens({
|
||||||
|
userId: user._id,
|
||||||
|
ip: req.realIP,
|
||||||
|
userAgent: req.headers["user-agent"] ?? ""
|
||||||
|
});
|
||||||
|
|
||||||
|
// store (refresh) token in httpOnly cookie
|
||||||
|
res.cookie("jid", tokens.refreshToken, {
|
||||||
|
httpOnly: true,
|
||||||
|
path: "/",
|
||||||
|
sameSite: "strict",
|
||||||
|
secure: await getHttpsEnabled()
|
||||||
|
});
|
||||||
|
|
||||||
|
// case: user does not have MFA enablgged
|
||||||
|
// return (access) token in response
|
||||||
|
|
||||||
|
interface ResponseData {
|
||||||
|
mfaEnabled: boolean;
|
||||||
|
encryptionVersion: any;
|
||||||
|
protectedKey?: string;
|
||||||
|
protectedKeyIV?: string;
|
||||||
|
protectedKeyTag?: string;
|
||||||
|
token: string;
|
||||||
|
publicKey?: string;
|
||||||
|
encryptedPrivateKey?: string;
|
||||||
|
iv?: string;
|
||||||
|
tag?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response: ResponseData = {
|
||||||
|
mfaEnabled: false,
|
||||||
|
encryptionVersion: user.encryptionVersion,
|
||||||
|
token: tokens.token,
|
||||||
|
publicKey: user.publicKey,
|
||||||
|
encryptedPrivateKey: user.encryptedPrivateKey,
|
||||||
|
iv: user.iv,
|
||||||
|
tag: user.tag
|
||||||
|
};
|
||||||
|
|
||||||
|
if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) {
|
||||||
|
response.protectedKey = user.protectedKey;
|
||||||
|
response.protectedKeyIV = user.protectedKeyIV;
|
||||||
|
response.protectedKeyTag = user.protectedKeyTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginAction = await EELogService.createAction({
|
||||||
|
name: ACTION_LOGIN,
|
||||||
|
userId: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
loginAction &&
|
||||||
|
(await EELogService.createLog({
|
||||||
|
userId: user._id,
|
||||||
|
actions: [loginAction],
|
||||||
|
channel: getUserAgentType(req.headers["user-agent"]),
|
||||||
|
ipAddress: req.realIP
|
||||||
|
}));
|
||||||
|
|
||||||
|
return res.status(200).send(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(400).send({
|
||||||
|
message: "Failed to authenticate. Try again?"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,8 +9,14 @@ import { getAllImportedSecrets } from "../../services/SecretImportService";
|
|||||||
import { Folder, IServiceTokenData } from "../../models";
|
import { Folder, IServiceTokenData } from "../../models";
|
||||||
import { getFolderByPath } from "../../services/FolderService";
|
import { getFolderByPath } from "../../services/FolderService";
|
||||||
import { BadRequestError } from "../../utils/errors";
|
import { BadRequestError } from "../../utils/errors";
|
||||||
import { requireWorkspaceAuth } from "../../middleware";
|
import { validateRequest } from "../../helpers/validation";
|
||||||
import { ADMIN, MEMBER, PERMISSION_READ_SECRETS } from "../../variables";
|
import * as reqValidator from "../../validation/secrets";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../services/ProjectRoleService";
|
||||||
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return secrets for workspace with id [workspaceId] and environment
|
* Return secrets for workspace with id [workspaceId] and environment
|
||||||
@@ -19,31 +25,29 @@ import { ADMIN, MEMBER, PERMISSION_READ_SECRETS } from "../../variables";
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getSecretsRaw = async (req: Request, res: Response) => {
|
export const getSecretsRaw = async (req: Request, res: Response) => {
|
||||||
let workspaceId = req.query.workspaceId as string;
|
let {
|
||||||
let environment = req.query.environment as string;
|
query: { secretPath, environment, workspaceId, include_imports: includeImports, folderId }
|
||||||
let secretPath = req.query.secretPath as string;
|
} = await validateRequest(reqValidator.GetSecretsRawV3, req);
|
||||||
const folderId = req.query.folderId as string | undefined;
|
|
||||||
const includeImports = req.query.include_imports as string;
|
|
||||||
|
|
||||||
// if the service token has single scope, it will get all secrets for that scope by default
|
// if the service token has single scope, it will get all secrets for that scope by default
|
||||||
const serviceTokenDetails: IServiceTokenData = req?.serviceTokenData;
|
const serviceTokenDetails: IServiceTokenData = req?.serviceTokenData;
|
||||||
if (serviceTokenDetails && serviceTokenDetails.scopes.length == 1 && !containsGlobPatterns(serviceTokenDetails.scopes[0].secretPath)) {
|
if (
|
||||||
|
serviceTokenDetails &&
|
||||||
|
serviceTokenDetails.scopes.length == 1 &&
|
||||||
|
!containsGlobPatterns(serviceTokenDetails.scopes[0].secretPath)
|
||||||
|
) {
|
||||||
const scope = serviceTokenDetails.scopes[0];
|
const scope = serviceTokenDetails.scopes[0];
|
||||||
secretPath = scope.secretPath;
|
secretPath = scope.secretPath;
|
||||||
environment = scope.environment;
|
environment = scope.environment;
|
||||||
workspaceId = serviceTokenDetails.workspace.toString();
|
workspaceId = serviceTokenDetails.workspace.toString();
|
||||||
} else {
|
} else {
|
||||||
requireWorkspaceAuth({
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
locationWorkspaceId: "query",
|
ProjectPermissionActions.Read,
|
||||||
locationEnvironment: "query",
|
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||||
requiredPermissions: [PERMISSION_READ_SECRETS],
|
);
|
||||||
requireBlindIndicesEnabled: true,
|
|
||||||
requireE2EEOff: true
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const secrets = await SecretService.getSecrets({
|
const secrets = await SecretService.getSecrets({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
@@ -56,7 +60,7 @@ export const getSecretsRaw = async (req: Request, res: Response) => {
|
|||||||
workspaceId: new Types.ObjectId(workspaceId)
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (includeImports === "true") {
|
if (includeImports) {
|
||||||
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
||||||
let folderId = "root";
|
let folderId = "root";
|
||||||
// if folder exist get it and replace folderid with new one
|
// if folder exist get it and replace folderid with new one
|
||||||
@@ -99,11 +103,18 @@ export const getSecretsRaw = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getSecretByNameRaw = async (req: Request, res: Response) => {
|
export const getSecretByNameRaw = async (req: Request, res: Response) => {
|
||||||
const { secretName } = req.params;
|
const {
|
||||||
const workspaceId = req.query.workspaceId as string;
|
query: { secretPath, environment, workspaceId, type },
|
||||||
const environment = req.query.environment as string;
|
params: { secretName }
|
||||||
const secretPath = req.query.secretPath as string;
|
} = await validateRequest(reqValidator.GetSecretByNameRawV3, req);
|
||||||
const type = req.query.type as "shared" | "personal" | undefined;
|
|
||||||
|
if (req.user._id) {
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const secret = await SecretService.getSecret({
|
const secret = await SecretService.getSecret({
|
||||||
secretName,
|
secretName,
|
||||||
@@ -132,8 +143,18 @@ export const getSecretByNameRaw = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const createSecretRaw = async (req: Request, res: Response) => {
|
export const createSecretRaw = async (req: Request, res: Response) => {
|
||||||
const { secretName } = req.params;
|
const {
|
||||||
const { workspaceId, environment, type, secretValue, secretComment, secretPath = "/" } = req.body;
|
params: { secretName },
|
||||||
|
body: { secretPath, environment, workspaceId, type, secretValue, secretComment }
|
||||||
|
} = await validateRequest(reqValidator.CreateSecretRawV3, req);
|
||||||
|
|
||||||
|
if (req.user._id) {
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const key = await BotService.getWorkspaceKeyWithBot({
|
const key = await BotService.getWorkspaceKeyWithBot({
|
||||||
workspaceId: new Types.ObjectId(workspaceId)
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
@@ -197,8 +218,18 @@ export const createSecretRaw = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const updateSecretByNameRaw = async (req: Request, res: Response) => {
|
export const updateSecretByNameRaw = async (req: Request, res: Response) => {
|
||||||
const { secretName } = req.params;
|
const {
|
||||||
const { workspaceId, environment, type, secretValue, secretPath = "/" } = req.body;
|
params: { secretName },
|
||||||
|
body: { secretValue, environment, secretPath, type, workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.UpdateSecretByNameRawV3, req);
|
||||||
|
|
||||||
|
if (req.user._id) {
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const key = await BotService.getWorkspaceKeyWithBot({
|
const key = await BotService.getWorkspaceKeyWithBot({
|
||||||
workspaceId: new Types.ObjectId(workspaceId)
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
@@ -211,7 +242,7 @@ export const updateSecretByNameRaw = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
const secret = await SecretService.updateSecret({
|
const secret = await SecretService.updateSecret({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId,
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
authData: req.authData,
|
authData: req.authData,
|
||||||
@@ -243,12 +274,22 @@ export const updateSecretByNameRaw = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const deleteSecretByNameRaw = async (req: Request, res: Response) => {
|
export const deleteSecretByNameRaw = async (req: Request, res: Response) => {
|
||||||
const { secretName } = req.params;
|
const {
|
||||||
const { workspaceId, environment, type, secretPath = "/" } = req.body;
|
params: { secretName },
|
||||||
|
body: { environment, secretPath, type, workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.DeleteSecretByNameRawV3, req);
|
||||||
|
|
||||||
|
if (req.user._id) {
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const { secret } = await SecretService.deleteSecret({
|
const { secret } = await SecretService.deleteSecret({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId,
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
authData: req.authData,
|
authData: req.authData,
|
||||||
@@ -282,11 +323,17 @@ export const deleteSecretByNameRaw = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getSecrets = async (req: Request, res: Response) => {
|
export const getSecrets = async (req: Request, res: Response) => {
|
||||||
const workspaceId = req.query.workspaceId as string;
|
const {
|
||||||
const environment = req.query.environment as string;
|
query: { secretPath, environment, workspaceId, include_imports: includeImports,folderId }
|
||||||
const secretPath = req.query.secretPath as string;
|
} = await validateRequest(reqValidator.GetSecretsV3, req);
|
||||||
const folderId = req.query.folderId as string | undefined;
|
|
||||||
const includeImports = req.query.include_imports as string;
|
if (req.user._id) {
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const secrets = await SecretService.getSecrets({
|
const secrets = await SecretService.getSecrets({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
@@ -296,7 +343,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
authData: req.authData
|
authData: req.authData
|
||||||
});
|
});
|
||||||
|
|
||||||
if (includeImports === "true") {
|
if (includeImports) {
|
||||||
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
const folders = await Folder.findOne({ workspace: workspaceId, environment });
|
||||||
let folderId = "root";
|
let folderId = "root";
|
||||||
// if folder exist get it and replace folderid with new one
|
// if folder exist get it and replace folderid with new one
|
||||||
@@ -325,11 +372,18 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getSecretByName = async (req: Request, res: Response) => {
|
export const getSecretByName = async (req: Request, res: Response) => {
|
||||||
const { secretName } = req.params;
|
const {
|
||||||
const workspaceId = req.query.workspaceId as string;
|
query: { secretPath, environment, workspaceId, type },
|
||||||
const environment = req.query.environment as string;
|
params: { secretName }
|
||||||
const secretPath = req.query.secretPath as string;
|
} = await validateRequest(reqValidator.GetSecretByNameV3, req);
|
||||||
const type = req.query.type as "shared" | "personal" | undefined;
|
|
||||||
|
if (req.user._id) {
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const secret = await SecretService.getSecret({
|
const secret = await SecretService.getSecret({
|
||||||
secretName,
|
secretName,
|
||||||
@@ -351,23 +405,33 @@ export const getSecretByName = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const createSecret = async (req: Request, res: Response) => {
|
export const createSecret = async (req: Request, res: Response) => {
|
||||||
const { secretName } = req.params;
|
|
||||||
const {
|
const {
|
||||||
workspaceId,
|
body: {
|
||||||
environment,
|
workspaceId,
|
||||||
type,
|
secretName,
|
||||||
secretKeyCiphertext,
|
secretPath,
|
||||||
secretKeyIV,
|
environment,
|
||||||
secretKeyTag,
|
metadata,
|
||||||
secretValueCiphertext,
|
type,
|
||||||
secretValueIV,
|
secretKeyIV,
|
||||||
secretValueTag,
|
secretKeyTag,
|
||||||
secretCommentCiphertext,
|
secretValueIV,
|
||||||
secretCommentIV,
|
secretValueTag,
|
||||||
secretCommentTag,
|
secretCommentIV,
|
||||||
secretPath = "/",
|
secretCommentTag,
|
||||||
metadata
|
secretKeyCiphertext,
|
||||||
} = req.body;
|
secretValueCiphertext,
|
||||||
|
secretCommentCiphertext
|
||||||
|
}
|
||||||
|
} = await validateRequest(reqValidator.CreateSecretV3, req);
|
||||||
|
|
||||||
|
if (req.user._id) {
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const secret = await SecretService.createSecret({
|
const secret = await SecretService.createSecret({
|
||||||
secretName,
|
secretName,
|
||||||
@@ -410,20 +474,30 @@ export const createSecret = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const updateSecretByName = async (req: Request, res: Response) => {
|
export const updateSecretByName = async (req: Request, res: Response) => {
|
||||||
const { secretName } = req.params;
|
|
||||||
const {
|
const {
|
||||||
workspaceId,
|
body: {
|
||||||
environment,
|
secretValueCiphertext,
|
||||||
type,
|
secretValueTag,
|
||||||
secretValueCiphertext,
|
secretValueIV,
|
||||||
secretValueIV,
|
type,
|
||||||
secretValueTag,
|
environment,
|
||||||
secretPath = "/"
|
secretPath,
|
||||||
} = req.body;
|
workspaceId
|
||||||
|
},
|
||||||
|
params: { secretName }
|
||||||
|
} = await validateRequest(reqValidator.UpdateSecretByNameV3, req);
|
||||||
|
|
||||||
|
if (req.user._id) {
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const secret = await SecretService.updateSecret({
|
const secret = await SecretService.updateSecret({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId,
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
authData: req.authData,
|
authData: req.authData,
|
||||||
@@ -452,12 +526,22 @@ export const updateSecretByName = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const deleteSecretByName = async (req: Request, res: Response) => {
|
export const deleteSecretByName = async (req: Request, res: Response) => {
|
||||||
const { secretName } = req.params;
|
const {
|
||||||
const { workspaceId, environment, type, secretPath = "/" } = req.body;
|
body: { type, environment, secretPath, workspaceId },
|
||||||
|
params: { secretName }
|
||||||
|
} = await validateRequest(reqValidator.DeleteSecretByNameV3, req);
|
||||||
|
|
||||||
|
if (req.user._id) {
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const { secret } = await SecretService.deleteSecret({
|
const { secret } = await SecretService.deleteSecret({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId,
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
authData: req.authData,
|
authData: req.authData,
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import { Request, Response } from "express";
|
|||||||
import * as Sentry from "@sentry/node";
|
import * as Sentry from "@sentry/node";
|
||||||
import { MembershipOrg, User } from "../../models";
|
import { MembershipOrg, User } from "../../models";
|
||||||
import { completeAccount } from "../../helpers/user";
|
import { completeAccount } from "../../helpers/user";
|
||||||
import {
|
import { initializeDefaultOrg } from "../../helpers/signup";
|
||||||
initializeDefaultOrg,
|
|
||||||
} from "../../helpers/signup";
|
|
||||||
import { issueAuthTokens, validateProviderAuthToken } from "../../helpers/auth";
|
import { issueAuthTokens, validateProviderAuthToken } from "../../helpers/auth";
|
||||||
import { ACCEPTED, INVITED } from "../../variables";
|
import { ACCEPTED, INVITED } from "../../variables";
|
||||||
import { standardRequest } from "../../config/request";
|
import { standardRequest } from "../../config/request";
|
||||||
@@ -13,6 +11,8 @@ import { getHttpsEnabled, getJwtSignupSecret, getLoopsApiKey } from "../../confi
|
|||||||
import { BadRequestError } from "../../utils/errors";
|
import { BadRequestError } from "../../utils/errors";
|
||||||
import { TelemetryService } from "../../services";
|
import { TelemetryService } from "../../services";
|
||||||
import { AuthMethod } from "../../models";
|
import { AuthMethod } from "../../models";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../validation/auth";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Complete setting up user by adding their personal and auth information as part of the
|
* Complete setting up user by adding their personal and auth information as part of the
|
||||||
@@ -22,177 +22,174 @@ import { AuthMethod } from "../../models";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const completeAccountSignup = async (req: Request, res: Response) => {
|
export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||||
let user, token, refreshToken;
|
let user, token;
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
email,
|
body: {
|
||||||
firstName,
|
email,
|
||||||
lastName,
|
publicKey,
|
||||||
protectedKey,
|
salt,
|
||||||
protectedKeyIV,
|
lastName,
|
||||||
protectedKeyTag,
|
verifier,
|
||||||
publicKey,
|
firstName,
|
||||||
encryptedPrivateKey,
|
protectedKey,
|
||||||
encryptedPrivateKeyIV,
|
protectedKeyIV,
|
||||||
encryptedPrivateKeyTag,
|
protectedKeyTag,
|
||||||
salt,
|
organizationName,
|
||||||
verifier,
|
providerAuthToken,
|
||||||
organizationName,
|
attributionSource,
|
||||||
providerAuthToken,
|
encryptedPrivateKey,
|
||||||
attributionSource,
|
encryptedPrivateKeyIV,
|
||||||
}: {
|
encryptedPrivateKeyTag
|
||||||
email: string;
|
}
|
||||||
firstName: string;
|
} = await validateRequest(reqValidator.CompletedAccountSignupV3, req);
|
||||||
lastName: string;
|
|
||||||
protectedKey: string;
|
|
||||||
protectedKeyIV: string;
|
|
||||||
protectedKeyTag: string;
|
|
||||||
publicKey: string;
|
|
||||||
encryptedPrivateKey: string;
|
|
||||||
encryptedPrivateKeyIV: string;
|
|
||||||
encryptedPrivateKeyTag: string;
|
|
||||||
salt: string;
|
|
||||||
verifier: string;
|
|
||||||
organizationName: string;
|
|
||||||
providerAuthToken?: string;
|
|
||||||
attributionSource?: string;
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
user = await User.findOne({ email });
|
user = await User.findOne({ email });
|
||||||
|
|
||||||
if (!user || (user && user?.publicKey)) {
|
if (!user || (user && user?.publicKey)) {
|
||||||
// case 1: user doesn't exist.
|
// case 1: user doesn't exist.
|
||||||
// case 2: user has already completed account
|
// case 2: user has already completed account
|
||||||
return res.status(403).send({
|
return res.status(403).send({
|
||||||
error: "Failed to complete account for complete user",
|
error: "Failed to complete account for complete user"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (providerAuthToken) {
|
if (providerAuthToken) {
|
||||||
await validateProviderAuthToken({
|
await validateProviderAuthToken({
|
||||||
email,
|
email,
|
||||||
providerAuthToken
|
providerAuthToken,
|
||||||
});
|
user
|
||||||
} else {
|
});
|
||||||
const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>req.headers["authorization"]?.split(" ", 2) ?? [null, null]
|
} else {
|
||||||
if (AUTH_TOKEN_TYPE === null) {
|
const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>(
|
||||||
throw BadRequestError({ message: "Missing Authorization Header in the request header." });
|
req.headers["authorization"]?.split(" ", 2)
|
||||||
}
|
) ?? [null, null];
|
||||||
if (AUTH_TOKEN_TYPE.toLowerCase() !== "bearer") {
|
if (AUTH_TOKEN_TYPE === null) {
|
||||||
throw BadRequestError({ message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.` })
|
throw BadRequestError({ message: "Missing Authorization Header in the request header." });
|
||||||
}
|
}
|
||||||
if (AUTH_TOKEN_VALUE === null) {
|
if (AUTH_TOKEN_TYPE.toLowerCase() !== "bearer") {
|
||||||
throw BadRequestError({
|
throw BadRequestError({
|
||||||
message: "Missing Authorization Body in the request header",
|
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"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const decodedToken = <jwt.UserIDJwtPayload>(
|
const decodedToken = <jwt.UserIDJwtPayload>(
|
||||||
jwt.verify(AUTH_TOKEN_VALUE, await getJwtSignupSecret())
|
jwt.verify(AUTH_TOKEN_VALUE, await getJwtSignupSecret())
|
||||||
);
|
);
|
||||||
|
|
||||||
if (decodedToken.userId !== user.id) {
|
if (decodedToken.userId !== user.id) {
|
||||||
throw BadRequestError();
|
throw BadRequestError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// complete setting up user's account
|
// complete setting up user's account
|
||||||
user = await completeAccount({
|
user = await completeAccount({
|
||||||
userId: user._id.toString(),
|
userId: user._id.toString(),
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
encryptionVersion: 2,
|
encryptionVersion: 2,
|
||||||
protectedKey,
|
protectedKey,
|
||||||
protectedKeyIV,
|
protectedKeyIV,
|
||||||
protectedKeyTag,
|
protectedKeyTag,
|
||||||
publicKey,
|
publicKey,
|
||||||
encryptedPrivateKey,
|
encryptedPrivateKey,
|
||||||
encryptedPrivateKeyIV,
|
encryptedPrivateKeyIV,
|
||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
salt,
|
salt,
|
||||||
verifier,
|
verifier
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user)
|
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
|
|
||||||
|
|
||||||
const hasSamlEnabled = user.authMethods.some((authMethod: AuthMethod) => [AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes(authMethod));
|
const hasSamlEnabled = user.authMethods.some((authMethod: AuthMethod) =>
|
||||||
|
[AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes(authMethod)
|
||||||
|
);
|
||||||
|
|
||||||
if (!hasSamlEnabled) { // TODO: modify this part
|
if (!hasSamlEnabled) {
|
||||||
// initialize default organization and workspace
|
// TODO: modify this part
|
||||||
await initializeDefaultOrg({
|
// initialize default organization and workspace
|
||||||
organizationName,
|
await initializeDefaultOrg({
|
||||||
user,
|
organizationName,
|
||||||
});
|
user
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// update organization membership statuses that are
|
// update organization membership statuses that are
|
||||||
// invited to completed with user attached
|
// invited to completed with user attached
|
||||||
await MembershipOrg.updateMany(
|
await MembershipOrg.updateMany(
|
||||||
{
|
{
|
||||||
inviteEmail: email,
|
inviteEmail: email,
|
||||||
status: INVITED,
|
status: INVITED
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
user,
|
user,
|
||||||
status: ACCEPTED,
|
status: ACCEPTED
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// issue tokens
|
// issue tokens
|
||||||
const tokens = await issueAuthTokens({
|
const tokens = await issueAuthTokens({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
ip: req.realIP,
|
ip: req.realIP,
|
||||||
userAgent: req.headers["user-agent"] ?? "",
|
userAgent: req.headers["user-agent"] ?? ""
|
||||||
});
|
});
|
||||||
|
|
||||||
token = tokens.token;
|
token = tokens.token;
|
||||||
|
|
||||||
// sending a welcome email to new users
|
// sending a welcome email to new users
|
||||||
if (await getLoopsApiKey()) {
|
if (await getLoopsApiKey()) {
|
||||||
await standardRequest.post("https://app.loops.so/api/v1/events/send", {
|
await standardRequest.post(
|
||||||
"email": email,
|
"https://app.loops.so/api/v1/events/send",
|
||||||
"eventName": "Sign Up",
|
{
|
||||||
"firstName": firstName,
|
email: email,
|
||||||
"lastName": lastName,
|
eventName: "Sign Up",
|
||||||
}, {
|
firstName: firstName,
|
||||||
headers: {
|
lastName: lastName
|
||||||
"Accept": "application/json",
|
},
|
||||||
"Authorization": "Bearer " + (await getLoopsApiKey()),
|
{
|
||||||
},
|
headers: {
|
||||||
});
|
Accept: "application/json",
|
||||||
}
|
Authorization: "Bearer " + (await getLoopsApiKey())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// store (refresh) token in httpOnly cookie
|
// store (refresh) token in httpOnly cookie
|
||||||
res.cookie("jid", tokens.refreshToken, {
|
res.cookie("jid", tokens.refreshToken, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: "/",
|
path: "/",
|
||||||
sameSite: "strict",
|
sameSite: "strict",
|
||||||
secure: await getHttpsEnabled(),
|
secure: await getHttpsEnabled()
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = await TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: "User Signed Up",
|
event: "User Signed Up",
|
||||||
distinctId: email,
|
distinctId: email,
|
||||||
properties: {
|
properties: {
|
||||||
email,
|
email,
|
||||||
...(attributionSource ? { attributionSource } : {})
|
...(attributionSource ? { attributionSource } : {})
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
message: "Failed to complete account setup",
|
message: "Failed to complete account setup"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: "Successfully set up account",
|
message: "Successfully set up account",
|
||||||
user,
|
user,
|
||||||
token,
|
token
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
|
import { validateRequest } from "../../helpers/validation";
|
||||||
import { Secret } from "../../models";
|
import { Secret } from "../../models";
|
||||||
import { SecretService } from"../../services";
|
import { SecretService } from "../../services";
|
||||||
|
import { getUserProjectPermissions } from "../../services/ProjectRoleService";
|
||||||
|
import { UnauthorizedRequestError } from "../../utils/errors";
|
||||||
|
import * as reqValidator from "../../validation/workspace";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether or not all secrets in workspace with id [workspaceId]
|
* Return whether or not all secrets in workspace with id [workspaceId]
|
||||||
@@ -11,32 +15,44 @@ import { SecretService } from"../../services";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getWorkspaceBlindIndexStatus = async (req: Request, res: Response) => {
|
export const getWorkspaceBlindIndexStatus = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetWorkspaceBlinkIndexStatusV3, req);
|
||||||
|
|
||||||
const secretsWithoutBlindIndex = await Secret.countDocuments({
|
const { membership } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
if (membership.role !== "admin")
|
||||||
secretBlindIndex: {
|
throw UnauthorizedRequestError({ message: "User must be an admin" });
|
||||||
$exists: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(200).send(secretsWithoutBlindIndex === 0);
|
const secretsWithoutBlindIndex = await Secret.countDocuments({
|
||||||
}
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
|
secretBlindIndex: {
|
||||||
|
$exists: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send(secretsWithoutBlindIndex === 0);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all secrets for workspace with id [workspaceId]
|
* Get all secrets for workspace with id [workspaceId]
|
||||||
*/
|
*/
|
||||||
export const getWorkspaceSecrets = async (req: Request, res: Response) => {
|
export const getWorkspaceSecrets = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(reqValidator.GetWorkspaceSecretsV3, req);
|
||||||
|
|
||||||
const secrets = await Secret.find({
|
const { membership } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
workspace: new Types.ObjectId (workspaceId),
|
if (membership.role !== "admin")
|
||||||
});
|
throw UnauthorizedRequestError({ message: "User must be an admin" });
|
||||||
|
|
||||||
return res.status(200).send({
|
const secrets = await Secret.find({
|
||||||
secrets,
|
workspace: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
return res.status(200).send({
|
||||||
|
secrets
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update blind indices for secrets in workspace with id [workspaceId]
|
* Update blind indices for secrets in workspace with id [workspaceId]
|
||||||
@@ -44,47 +60,44 @@ export const getWorkspaceSecrets = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const nameWorkspaceSecrets = async (req: Request, res: Response) => {
|
export const nameWorkspaceSecrets = async (req: Request, res: Response) => {
|
||||||
interface SecretToUpdate {
|
const {
|
||||||
secretName: string;
|
params: { workspaceId },
|
||||||
_id: string;
|
body: { secretsToUpdate }
|
||||||
}
|
} = await validateRequest(reqValidator.NameWorkspaceSecretsV3, req);
|
||||||
|
|
||||||
const { workspaceId } = req.params;
|
const { membership } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
const {
|
if (membership.role !== "admin")
|
||||||
secretsToUpdate,
|
throw UnauthorizedRequestError({ message: "User must be an admin" });
|
||||||
}: {
|
|
||||||
secretsToUpdate: SecretToUpdate[];
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
// get secret blind index salt
|
// get secret blind index salt
|
||||||
const salt = await SecretService.getSecretBlindIndexSalt({
|
const salt = await SecretService.getSecretBlindIndexSalt({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
|
|
||||||
// update secret blind indices
|
// update secret blind indices
|
||||||
const operations = await Promise.all(
|
const operations = await Promise.all(
|
||||||
secretsToUpdate.map(async (secretToUpdate: SecretToUpdate) => {
|
secretsToUpdate.map(async (secretToUpdate) => {
|
||||||
const secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({
|
const secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({
|
||||||
secretName: secretToUpdate.secretName,
|
secretName: secretToUpdate.secretName,
|
||||||
salt,
|
salt
|
||||||
});
|
});
|
||||||
|
|
||||||
return ({
|
return {
|
||||||
updateOne: {
|
updateOne: {
|
||||||
filter: {
|
filter: {
|
||||||
_id: new Types.ObjectId(secretToUpdate._id),
|
_id: new Types.ObjectId(secretToUpdate._id)
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
secretBlindIndex,
|
secretBlindIndex
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
await Secret.bulkWrite(operations);
|
await Secret.bulkWrite(operations);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: "Successfully named workspace secrets",
|
message: "Successfully named workspace secrets"
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,30 +1,32 @@
|
|||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { Action } from "../../models";
|
import { Action } from "../../models";
|
||||||
import { ActionNotFoundError } from "../../../utils/errors";
|
import { ActionNotFoundError } from "../../../utils/errors";
|
||||||
|
import { validateRequest } from "../../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../../validation/action";
|
||||||
|
|
||||||
export const getAction = async (req: Request, res: Response) => {
|
export const getAction = async (req: Request, res: Response) => {
|
||||||
let action;
|
let action;
|
||||||
try {
|
try {
|
||||||
const { actionId } = req.params;
|
const {
|
||||||
|
params: { actionId }
|
||||||
|
} = await validateRequest(reqValidator.GetActionV1, req);
|
||||||
|
|
||||||
action = await Action
|
action = await Action.findById(actionId).populate([
|
||||||
.findById(actionId)
|
"payload.secretVersions.oldSecretVersion",
|
||||||
.populate([
|
"payload.secretVersions.newSecretVersion"
|
||||||
"payload.secretVersions.oldSecretVersion",
|
]);
|
||||||
"payload.secretVersions.newSecretVersion",
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!action) throw ActionNotFoundError({
|
if (!action)
|
||||||
message: "Failed to find action",
|
throw ActionNotFoundError({
|
||||||
});
|
message: "Failed to find action"
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw ActionNotFoundError({
|
throw ActionNotFoundError({
|
||||||
message: "Failed to find action",
|
message: "Failed to find action"
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(200).send({
|
|
||||||
action,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
action
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { Request, Response } from "express";
|
|||||||
import { EELicenseService } from "../../services";
|
import { EELicenseService } from "../../services";
|
||||||
import { getLicenseServerUrl } from "../../../config";
|
import { getLicenseServerUrl } from "../../../config";
|
||||||
import { licenseServerKeyRequest } from "../../../config/request";
|
import { licenseServerKeyRequest } from "../../../config/request";
|
||||||
|
import { validateRequest } from "../../../helpers/validation";
|
||||||
|
import * as reqValidator from "../../../validation/cloudProducts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return available cloud product information.
|
* Return available cloud product information.
|
||||||
@@ -11,18 +13,20 @@ import { licenseServerKeyRequest } from "../../../config/request";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getCloudProducts = async (req: Request, res: Response) => {
|
export const getCloudProducts = async (req: Request, res: Response) => {
|
||||||
const billingCycle = req.query["billing-cycle"] as string;
|
const {
|
||||||
|
query: { "billing-cycle": billingCycle }
|
||||||
|
} = await validateRequest(reqValidator.GetCloudProductsV1, req);
|
||||||
|
|
||||||
if (EELicenseService.instanceType === "cloud") {
|
if (EELicenseService.instanceType === "cloud") {
|
||||||
const { data } = await licenseServerKeyRequest.get(
|
const { data } = await licenseServerKeyRequest.get(
|
||||||
`${await getLicenseServerUrl()}/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
|
`${await getLicenseServerUrl()}/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).send(data);
|
return res.status(200).send(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
head: [],
|
head: [],
|
||||||
rows: [],
|
rows: []
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
|
import { validateRequest } from "../../../helpers/validation";
|
||||||
import { Secret } from "../../../models";
|
import { Secret } from "../../../models";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../../services/ProjectRoleService";
|
||||||
|
import { BadRequestError } from "../../../utils/errors";
|
||||||
|
import * as reqValidator from "../../../validation";
|
||||||
import { SecretVersion } from "../../models";
|
import { SecretVersion } from "../../models";
|
||||||
import { EESecretService } from "../../services";
|
import { EESecretService } from "../../services";
|
||||||
|
|
||||||
@@ -54,10 +63,21 @@ export const getSecretVersions = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
const { secretId } = req.params;
|
const {
|
||||||
|
params: { secretId },
|
||||||
|
query: { offset, limit }
|
||||||
|
} = await validateRequest(reqValidator.GetSecretVersionsV1, req);
|
||||||
|
|
||||||
const offset: number = parseInt(req.query.offset as string);
|
const secret = await Secret.findById(secretId);
|
||||||
const limit: number = parseInt(req.query.limit as string);
|
if (!secret) {
|
||||||
|
throw BadRequestError({ message: "Failed to find secret" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, secret.workspace.toString());
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.SecretRollback
|
||||||
|
);
|
||||||
|
|
||||||
const secretVersions = await SecretVersion.find({
|
const secretVersions = await SecretVersion.find({
|
||||||
secret: secretId
|
secret: secretId
|
||||||
@@ -126,8 +146,28 @@ export const rollbackSecretVersion = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
const { secretId } = req.params;
|
|
||||||
const { version } = req.body;
|
const {
|
||||||
|
params: { secretId },
|
||||||
|
body: { version }
|
||||||
|
} = await validateRequest(reqValidator.RollbackSecretVersionV1, req);
|
||||||
|
|
||||||
|
const toBeUpdatedSec = await Secret.findById(secretId);
|
||||||
|
if (!toBeUpdatedSec) {
|
||||||
|
throw BadRequestError({ message: "Failed to find secret" });
|
||||||
|
}
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
toBeUpdatedSec.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionSub.SecretRollback
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
subject(ProjectPermissionSub.Secrets, { environment: toBeUpdatedSec.environment })
|
||||||
|
);
|
||||||
|
|
||||||
// validate secret version
|
// validate secret version
|
||||||
const oldSecretVersion = await SecretVersion.findOne({
|
const oldSecretVersion = await SecretVersion.findOne({
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
|
import { validateRequest } from "../../../helpers/validation";
|
||||||
import {
|
import {
|
||||||
ISecretVersion,
|
ProjectPermissionActions,
|
||||||
SecretSnapshot,
|
ProjectPermissionSub,
|
||||||
TFolderRootVersionSchema,
|
getUserProjectPermissions
|
||||||
} from "../../models";
|
} from "../../../services/ProjectRoleService";
|
||||||
|
import * as reqValidator from "../../../validation/secretSnapshot";
|
||||||
|
import { ISecretVersion, SecretSnapshot, TFolderRootVersionSchema } from "../../models";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return secret snapshot with id [secretSnapshotId]
|
* Return secret snapshot with id [secretSnapshotId]
|
||||||
@@ -12,7 +16,9 @@ import {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getSecretSnapshot = async (req: Request, res: Response) => {
|
export const getSecretSnapshot = async (req: Request, res: Response) => {
|
||||||
const { secretSnapshotId } = req.params;
|
const {
|
||||||
|
params: { secretSnapshotId }
|
||||||
|
} = await validateRequest(reqValidator.GetSecretSnapshotV1, req);
|
||||||
|
|
||||||
const secretSnapshot = await SecretSnapshot.findById(secretSnapshotId)
|
const secretSnapshot = await SecretSnapshot.findById(secretSnapshotId)
|
||||||
.lean()
|
.lean()
|
||||||
@@ -20,26 +26,36 @@ export const getSecretSnapshot = async (req: Request, res: Response) => {
|
|||||||
path: "secretVersions",
|
path: "secretVersions",
|
||||||
populate: {
|
populate: {
|
||||||
path: "tags",
|
path: "tags",
|
||||||
model: "Tag",
|
model: "Tag"
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
.populate<{ folderVersion: TFolderRootVersionSchema }>("folderVersion");
|
.populate<{ folderVersion: TFolderRootVersionSchema }>("folderVersion");
|
||||||
|
|
||||||
if (!secretSnapshot) throw new Error("Failed to find secret snapshot");
|
if (!secretSnapshot) throw new Error("Failed to find secret snapshot");
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(
|
||||||
|
req.user._id,
|
||||||
|
secretSnapshot.workspace.toString()
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.SecretRollback
|
||||||
|
);
|
||||||
|
|
||||||
const folderId = secretSnapshot.folderId;
|
const folderId = secretSnapshot.folderId;
|
||||||
// to show only the folder required secrets
|
// to show only the folder required secrets
|
||||||
secretSnapshot.secretVersions = secretSnapshot.secretVersions.filter(
|
secretSnapshot.secretVersions = secretSnapshot.secretVersions.filter(
|
||||||
({ folder }) => folder === folderId
|
({ folder }) => folder === folderId
|
||||||
);
|
);
|
||||||
|
|
||||||
secretSnapshot.folderVersion =
|
secretSnapshot.folderVersion = secretSnapshot?.folderVersion?.nodes?.children?.map(
|
||||||
secretSnapshot?.folderVersion?.nodes?.children?.map(({ id, name }) => ({
|
({ id, name }) => ({
|
||||||
id,
|
id,
|
||||||
name,
|
name
|
||||||
})) as any;
|
})
|
||||||
|
) as any;
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secretSnapshot,
|
secretSnapshot
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,16 +22,32 @@ import { getLatestSecretVersionIds } from "../../helpers/secretVersion";
|
|||||||
import { searchByFolderId } from "../../../services/FolderService";
|
import { searchByFolderId } from "../../../services/FolderService";
|
||||||
import { EEAuditLogService, EELicenseService } from "../../services";
|
import { EEAuditLogService, EELicenseService } from "../../services";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "../../../utils/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "../../../utils/ip";
|
||||||
|
import { validateRequest } from "../../../helpers/validation";
|
||||||
|
import {
|
||||||
|
AddWorkspaceTrustedIpV1,
|
||||||
|
DeleteWorkspaceTrustedIpV1,
|
||||||
|
GetWorkspaceAuditLogActorFilterOptsV1,
|
||||||
|
GetWorkspaceAuditLogsV1,
|
||||||
|
GetWorkspaceLogsV1,
|
||||||
|
GetWorkspaceSecretSnapshotsCountV1,
|
||||||
|
GetWorkspaceSecretSnapshotsV1,
|
||||||
|
GetWorkspaceTrustedIpsV1,
|
||||||
|
RollbackWorkspaceSecretSnapshotV1,
|
||||||
|
UpdateWorkspaceTrustedIpV1
|
||||||
|
} from "../../../validation";
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionSub,
|
||||||
|
getUserProjectPermissions
|
||||||
|
} from "../../../services/ProjectRoleService";
|
||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return secret snapshots for workspace with id [workspaceId]
|
* Return secret snapshots for workspace with id [workspaceId]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getWorkspaceSecretSnapshots = async (
|
export const getWorkspaceSecretSnapshots = async (req: Request, res: Response) => {
|
||||||
req: Request,
|
|
||||||
res: Response
|
|
||||||
) => {
|
|
||||||
/*
|
/*
|
||||||
#swagger.summary = 'Return project secret snapshot ids'
|
#swagger.summary = 'Return project secret snapshot ids'
|
||||||
#swagger.description = 'Return project secret snapshots ids'
|
#swagger.description = 'Return project secret snapshots ids'
|
||||||
@@ -77,23 +93,28 @@ export const getWorkspaceSecretSnapshots = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
const { environment, folderId } = req.query;
|
params: { workspaceId },
|
||||||
|
query: { environment, folderId, offset, limit }
|
||||||
|
} = await validateRequest(GetWorkspaceSecretSnapshotsV1, req);
|
||||||
|
|
||||||
const offset: number = parseInt(req.query.offset as string);
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
const limit: number = parseInt(req.query.limit as string);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.SecretRollback
|
||||||
|
);
|
||||||
|
|
||||||
const secretSnapshots = await SecretSnapshot.find({
|
const secretSnapshots = await SecretSnapshot.find({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment,
|
environment,
|
||||||
folderId: folderId || "root",
|
folderId: folderId || "root"
|
||||||
})
|
})
|
||||||
.sort({ createdAt: -1 })
|
.sort({ createdAt: -1 })
|
||||||
.skip(offset)
|
.skip(offset)
|
||||||
.limit(limit);
|
.limit(limit);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secretSnapshots,
|
secretSnapshots
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -102,21 +123,26 @@ export const getWorkspaceSecretSnapshots = async (
|
|||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getWorkspaceSecretSnapshotsCount = async (
|
export const getWorkspaceSecretSnapshotsCount = async (req: Request, res: Response) => {
|
||||||
req: Request,
|
const {
|
||||||
res: Response
|
params: { workspaceId },
|
||||||
) => {
|
query: { environment, folderId }
|
||||||
const { workspaceId } = req.params;
|
} = await validateRequest(GetWorkspaceSecretSnapshotsCountV1, req);
|
||||||
const { environment, folderId } = req.query;
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.SecretRollback
|
||||||
|
);
|
||||||
|
|
||||||
const count = await SecretSnapshot.countDocuments({
|
const count = await SecretSnapshot.countDocuments({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment,
|
environment,
|
||||||
folderId: folderId || "root",
|
folderId: folderId || "root"
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
count,
|
count
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -126,10 +152,7 @@ export const getWorkspaceSecretSnapshotsCount = async (
|
|||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const rollbackWorkspaceSecretSnapshot = async (
|
export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Response) => {
|
||||||
req: Request,
|
|
||||||
res: Response
|
|
||||||
) => {
|
|
||||||
/*
|
/*
|
||||||
#swagger.summary = 'Roll back project secrets to those captured in a secret snapshot version.'
|
#swagger.summary = 'Roll back project secrets to those captured in a secret snapshot version.'
|
||||||
#swagger.description = 'Roll back project secrets to those captured in a secret snapshot version.'
|
#swagger.description = 'Roll back project secrets to those captured in a secret snapshot version.'
|
||||||
@@ -181,19 +204,27 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
const { version, environment, folderId = "root" } = req.body;
|
params: { workspaceId },
|
||||||
|
body: { folderId, environment, version }
|
||||||
|
} = await validateRequest(RollbackWorkspaceSecretSnapshotV1, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionSub.SecretRollback
|
||||||
|
);
|
||||||
|
|
||||||
// validate secret snapshot
|
// validate secret snapshot
|
||||||
const secretSnapshot = await SecretSnapshot.findOne({
|
const secretSnapshot = await SecretSnapshot.findOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
version,
|
version,
|
||||||
environment,
|
environment,
|
||||||
folderId: folderId,
|
folderId: folderId
|
||||||
})
|
})
|
||||||
.populate<{ secretVersions: ISecretVersion[] }>({
|
.populate<{ secretVersions: ISecretVersion[] }>({
|
||||||
path: "secretVersions",
|
path: "secretVersions",
|
||||||
select: "+secretBlindIndex",
|
select: "+secretBlindIndex"
|
||||||
})
|
})
|
||||||
.populate<{ folderVersion: TFolderRootVersionSchema }>("folderVersion");
|
.populate<{ folderVersion: TFolderRootVersionSchema }>("folderVersion");
|
||||||
|
|
||||||
@@ -202,13 +233,13 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
const snapshotFolderTree = secretSnapshot.folderVersion;
|
const snapshotFolderTree = secretSnapshot.folderVersion;
|
||||||
const latestFolderTree = await Folder.findOne({
|
const latestFolderTree = await Folder.findOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment,
|
environment
|
||||||
});
|
});
|
||||||
|
|
||||||
const latestFolderVersion = await FolderVersion.findOne({
|
const latestFolderVersion = await FolderVersion.findOne({
|
||||||
environment,
|
environment,
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
"nodes.id": folderId,
|
"nodes.id": folderId
|
||||||
}).sort({ "nodes.version": -1 });
|
}).sort({ "nodes.version": -1 });
|
||||||
|
|
||||||
const oldSecretVersionsObj: Record<string, ISecretVersion> = {};
|
const oldSecretVersionsObj: Record<string, ISecretVersion> = {};
|
||||||
@@ -222,8 +253,7 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
|
|
||||||
// the parent node from current latest one
|
// the parent node from current latest one
|
||||||
// this will be modified according to the snapshot and latest snapshots
|
// this will be modified according to the snapshot and latest snapshots
|
||||||
const newFolderTree =
|
const newFolderTree = latestFolderTree && searchByFolderId(latestFolderTree.nodes, folderId);
|
||||||
latestFolderTree && searchByFolderId(latestFolderTree.nodes, folderId);
|
|
||||||
|
|
||||||
if (newFolderTree) {
|
if (newFolderTree) {
|
||||||
newFolderTree.children = snapshotFolderTree?.nodes?.children || [];
|
newFolderTree.children = snapshotFolderTree?.nodes?.children || [];
|
||||||
@@ -252,43 +282,43 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
folderId: {
|
folderId: {
|
||||||
$in: Object.keys(groupByFolderId),
|
$in: Object.keys(groupByFolderId)
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
const sortByFolderIdAndVersion: PipelineStage = {
|
const sortByFolderIdAndVersion: PipelineStage = {
|
||||||
$sort: { folderId: 1, version: -1 },
|
$sort: { folderId: 1, version: -1 }
|
||||||
};
|
};
|
||||||
const pickLatestVersionOfEachFolder = {
|
const pickLatestVersionOfEachFolder = {
|
||||||
$group: {
|
$group: {
|
||||||
_id: "$folderId",
|
_id: "$folderId",
|
||||||
latestVersion: { $first: "$version" },
|
latestVersion: { $first: "$version" },
|
||||||
doc: {
|
doc: {
|
||||||
$first: "$$ROOT",
|
$first: "$$ROOT"
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
const populateSecVersion = {
|
const populateSecVersion = {
|
||||||
$lookup: {
|
$lookup: {
|
||||||
from: SecretVersion.collection.name,
|
from: SecretVersion.collection.name,
|
||||||
localField: "doc.secretVersions",
|
localField: "doc.secretVersions",
|
||||||
foreignField: "_id",
|
foreignField: "_id",
|
||||||
as: "doc.secretVersions",
|
as: "doc.secretVersions"
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
const populateFolderVersion = {
|
const populateFolderVersion = {
|
||||||
$lookup: {
|
$lookup: {
|
||||||
from: FolderVersion.collection.name,
|
from: FolderVersion.collection.name,
|
||||||
localField: "doc.folderVersion",
|
localField: "doc.folderVersion",
|
||||||
foreignField: "_id",
|
foreignField: "_id",
|
||||||
as: "doc.folderVersion",
|
as: "doc.folderVersion"
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
const unwindFolderVerField = {
|
const unwindFolderVerField = {
|
||||||
$unwind: {
|
$unwind: {
|
||||||
path: "$doc.folderVersion",
|
path: "$doc.folderVersion",
|
||||||
preserveNullAndEmptyArrays: true,
|
preserveNullAndEmptyArrays: true
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
const latestSnapshotsByFolders: Array<{ doc: typeof secretSnapshot }> =
|
const latestSnapshotsByFolders: Array<{ doc: typeof secretSnapshot }> =
|
||||||
await SecretSnapshot.aggregate([
|
await SecretSnapshot.aggregate([
|
||||||
@@ -297,7 +327,7 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
pickLatestVersionOfEachFolder,
|
pickLatestVersionOfEachFolder,
|
||||||
populateSecVersion,
|
populateSecVersion,
|
||||||
populateFolderVersion,
|
populateFolderVersion,
|
||||||
unwindFolderVerField,
|
unwindFolderVerField
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// recursive snapshotting each level
|
// recursive snapshotting each level
|
||||||
@@ -327,7 +357,7 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
|
|
||||||
// TODO: fix any
|
// TODO: fix any
|
||||||
const latestSecretVersionIds = await getLatestSecretVersionIds({
|
const latestSecretVersionIds = await getLatestSecretVersionIds({
|
||||||
secretIds,
|
secretIds
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: fix any
|
// TODO: fix any
|
||||||
@@ -335,32 +365,31 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
await SecretVersion.find(
|
await SecretVersion.find(
|
||||||
{
|
{
|
||||||
_id: {
|
_id: {
|
||||||
$in: latestSecretVersionIds.map((s) => s.versionId),
|
$in: latestSecretVersionIds.map((s) => s.versionId)
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"secret version"
|
"secret version"
|
||||||
)
|
)
|
||||||
).reduce(
|
).reduce(
|
||||||
(accumulator, s) => ({
|
(accumulator, s) => ({
|
||||||
...accumulator,
|
...accumulator,
|
||||||
[`${s.secret.toString()}`]: s,
|
[`${s.secret.toString()}`]: s
|
||||||
}),
|
}),
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
const secDelQuery: Record<string, unknown> = {
|
const secDelQuery: Record<string, unknown> = {
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment,
|
environment
|
||||||
// undefined means root thus collect all secrets
|
// undefined means root thus collect all secrets
|
||||||
};
|
};
|
||||||
if (folderId !== "root" && folderIds.length)
|
if (folderId !== "root" && folderIds.length) secDelQuery.folder = { $in: folderIds };
|
||||||
secDelQuery.folder = { $in: folderIds };
|
|
||||||
|
|
||||||
// delete existing secrets
|
// delete existing secrets
|
||||||
await Secret.deleteMany(secDelQuery);
|
await Secret.deleteMany(secDelQuery);
|
||||||
await Folder.deleteOne({
|
await Folder.deleteOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment,
|
environment
|
||||||
});
|
});
|
||||||
|
|
||||||
// add secrets
|
// add secrets
|
||||||
@@ -382,7 +411,7 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
createdAt,
|
createdAt,
|
||||||
algorithm,
|
algorithm,
|
||||||
keyEncoding,
|
keyEncoding,
|
||||||
folder: secFolderId,
|
folder: secFolderId
|
||||||
} = oldSecretVersionsObj[sv];
|
} = oldSecretVersionsObj[sv];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -405,7 +434,7 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
createdAt,
|
createdAt,
|
||||||
algorithm,
|
algorithm,
|
||||||
keyEncoding,
|
keyEncoding,
|
||||||
folder: secFolderId,
|
folder: secFolderId
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -429,7 +458,7 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
secretValueTag,
|
secretValueTag,
|
||||||
algorithm,
|
algorithm,
|
||||||
keyEncoding,
|
keyEncoding,
|
||||||
folder: secFolderId,
|
folder: secFolderId
|
||||||
}) => ({
|
}) => ({
|
||||||
_id: new Types.ObjectId(),
|
_id: new Types.ObjectId(),
|
||||||
secret: _id,
|
secret: _id,
|
||||||
@@ -448,7 +477,7 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
secretValueTag,
|
secretValueTag,
|
||||||
algorithm,
|
algorithm,
|
||||||
keyEncoding,
|
keyEncoding,
|
||||||
folder: secFolderId,
|
folder: secFolderId
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -464,7 +493,7 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
const newFolderVersion = new FolderVersion({
|
const newFolderVersion = new FolderVersion({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment,
|
environment,
|
||||||
nodes: newFolderTree,
|
nodes: newFolderTree
|
||||||
});
|
});
|
||||||
await newFolderVersion.save();
|
await newFolderVersion.save();
|
||||||
}
|
}
|
||||||
@@ -473,13 +502,11 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
await SecretVersion.updateMany(
|
await SecretVersion.updateMany(
|
||||||
{
|
{
|
||||||
secret: {
|
secret: {
|
||||||
$in: Object.keys(oldSecretVersionsObj).map(
|
$in: Object.keys(oldSecretVersionsObj).map((sv) => oldSecretVersionsObj[sv].secret)
|
||||||
(sv) => oldSecretVersionsObj[sv].secret
|
}
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
isDeleted: false,
|
isDeleted: false
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -487,11 +514,11 @@ export const rollbackWorkspaceSecretSnapshot = async (
|
|||||||
await EESecretService.takeSecretSnapshot({
|
await EESecretService.takeSecretSnapshot({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
folderId,
|
folderId
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secrets,
|
secrets
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -568,13 +595,16 @@ export const getWorkspaceLogs = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
query: { limit, offset, userId, sortBy, actionNames },
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(GetWorkspaceLogsV1, req);
|
||||||
|
|
||||||
const offset: number = parseInt(req.query.offset as string);
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
const limit: number = parseInt(req.query.limit as string);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
const sortBy: string = req.query.sortBy as string;
|
ProjectPermissionActions.Read,
|
||||||
const userId: string = req.query.userId as string;
|
ProjectPermissionSub.AuditLogs
|
||||||
const actionNames: string = req.query.actionNames as string;
|
);
|
||||||
|
|
||||||
const logs = await Log.find({
|
const logs = await Log.find({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
@@ -582,10 +612,10 @@ export const getWorkspaceLogs = async (req: Request, res: Response) => {
|
|||||||
...(actionNames
|
...(actionNames
|
||||||
? {
|
? {
|
||||||
actionNames: {
|
actionNames: {
|
||||||
$in: actionNames.split(","),
|
$in: actionNames.split(",")
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
: {}),
|
: {})
|
||||||
})
|
})
|
||||||
.sort({ createdAt: sortBy === "recent" ? -1 : 1 })
|
.sort({ createdAt: sortBy === "recent" ? -1 : 1 })
|
||||||
.skip(offset)
|
.skip(offset)
|
||||||
@@ -594,7 +624,7 @@ export const getWorkspaceLogs = async (req: Request, res: Response) => {
|
|||||||
.populate("user serviceAccount serviceTokenData");
|
.populate("user serviceAccount serviceTokenData");
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
logs,
|
logs
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -604,44 +634,52 @@ export const getWorkspaceLogs = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
|
export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
const eventType = req.query.eventType;
|
query: { limit, offset, endDate, eventType, startDate, userAgentType, actor },
|
||||||
const userAgentType = req.query.userAgentType;
|
params: { workspaceId }
|
||||||
const actor = req.query.actor as string | undefined;
|
} = await validateRequest(GetWorkspaceAuditLogsV1, req);
|
||||||
const offset: number = parseInt(req.query.offset as string);
|
|
||||||
const limit: number = parseInt(req.query.limit as string);
|
|
||||||
|
|
||||||
const startDate = req.query.startDate as string;
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
const endDate = req.query.endDate as string;
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.AuditLogs
|
||||||
|
);
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
...(eventType ? {
|
...(eventType
|
||||||
"event.type": eventType
|
? {
|
||||||
} : {}),
|
"event.type": eventType
|
||||||
...(userAgentType ? {
|
}
|
||||||
userAgentType
|
: {}),
|
||||||
} : {}),
|
...(userAgentType
|
||||||
...(actor ? {
|
? {
|
||||||
"actor.type": actor.split("-", 2)[0],
|
userAgentType
|
||||||
...(actor.split("-", 2)[0] === ActorType.USER ? {
|
}
|
||||||
"actor.metadata.userId": actor.split("-", 2)[1]
|
: {}),
|
||||||
} : {
|
...(actor
|
||||||
"actor.metadata.serviceId": actor.split("-", 2)[1]
|
? {
|
||||||
})
|
"actor.type": actor.split("-", 2)[0],
|
||||||
} : {}),
|
...(actor.split("-", 2)[0] === ActorType.USER
|
||||||
...(startDate || endDate ? {
|
? {
|
||||||
createdAt: {
|
"actor.metadata.userId": actor.split("-", 2)[1]
|
||||||
...(startDate && { $gte: new Date(startDate) }),
|
}
|
||||||
...(endDate && { $lte: new Date(endDate) })
|
: {
|
||||||
}
|
"actor.metadata.serviceId": actor.split("-", 2)[1]
|
||||||
} : {})
|
})
|
||||||
}
|
}
|
||||||
|
: {}),
|
||||||
|
...(startDate || endDate
|
||||||
|
? {
|
||||||
|
createdAt: {
|
||||||
|
...(startDate && { $gte: new Date(startDate) }),
|
||||||
|
...(endDate && { $lte: new Date(endDate) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: {})
|
||||||
|
};
|
||||||
|
|
||||||
const auditLogs = await AuditLog.find(query)
|
const auditLogs = await AuditLog.find(query).sort({ createdAt: -1 }).skip(offset).limit(limit);
|
||||||
.sort({ createdAt: -1 })
|
|
||||||
.skip(offset)
|
|
||||||
.limit(limit);
|
|
||||||
|
|
||||||
const totalCount = await AuditLog.countDocuments(query);
|
const totalCount = await AuditLog.countDocuments(query);
|
||||||
|
|
||||||
@@ -649,7 +687,7 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
|
|||||||
auditLogs,
|
auditLogs,
|
||||||
totalCount
|
totalCount
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return audit log actor filter options for workspace with id [workspaceId]
|
* Return audit log actor filter options for workspace with id [workspaceId]
|
||||||
@@ -657,18 +695,26 @@ export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Response) => {
|
export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(GetWorkspaceAuditLogActorFilterOptsV1, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.AuditLogs
|
||||||
|
);
|
||||||
|
|
||||||
const userIds = await Membership.distinct("user", {
|
const userIds = await Membership.distinct("user", {
|
||||||
workspace: new Types.ObjectId(workspaceId)
|
workspace: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
const userActors: UserActor[] = (await User.find({
|
const userActors: UserActor[] = (
|
||||||
_id: {
|
await User.find({
|
||||||
$in: userIds
|
_id: {
|
||||||
}
|
$in: userIds
|
||||||
})
|
}
|
||||||
.select("email"))
|
}).select("email")
|
||||||
.map((user) => ({
|
).map((user) => ({
|
||||||
type: ActorType.USER,
|
type: ActorType.USER,
|
||||||
metadata: {
|
metadata: {
|
||||||
userId: user._id.toString(),
|
userId: user._id.toString(),
|
||||||
@@ -676,11 +722,11 @@ export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Res
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const serviceActors: ServiceActor[] = (await ServiceTokenData.find({
|
const serviceActors: ServiceActor[] = (
|
||||||
workspace: new Types.ObjectId(workspaceId)
|
await ServiceTokenData.find({
|
||||||
})
|
workspace: new Types.ObjectId(workspaceId)
|
||||||
.select("name"))
|
}).select("name")
|
||||||
.map((serviceTokenData) => ({
|
).map((serviceTokenData) => ({
|
||||||
type: ActorType.SERVICE,
|
type: ActorType.SERVICE,
|
||||||
metadata: {
|
metadata: {
|
||||||
serviceId: serviceTokenData._id.toString(),
|
serviceId: serviceTokenData._id.toString(),
|
||||||
@@ -691,7 +737,7 @@ export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Res
|
|||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
actors: [...userActors, ...serviceActors]
|
actors: [...userActors, ...serviceActors]
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return trusted ips for workspace with id [workspaceId]
|
* Return trusted ips for workspace with id [workspaceId]
|
||||||
@@ -699,7 +745,15 @@ export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Res
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getWorkspaceTrustedIps = async (req: Request, res: Response) => {
|
export const getWorkspaceTrustedIps = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
const {
|
||||||
|
params: { workspaceId }
|
||||||
|
} = await validateRequest(GetWorkspaceTrustedIpsV1, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.IpAllowList
|
||||||
|
);
|
||||||
|
|
||||||
const trustedIps = await TrustedIP.find({
|
const trustedIps = await TrustedIP.find({
|
||||||
workspace: new Types.ObjectId(workspaceId)
|
workspace: new Types.ObjectId(workspaceId)
|
||||||
@@ -708,7 +762,7 @@ export const getWorkspaceTrustedIps = async (req: Request, res: Response) => {
|
|||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
trustedIps
|
trustedIps
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a trusted ip to workspace with id [workspaceId]
|
* Add a trusted ip to workspace with id [workspaceId]
|
||||||
@@ -716,24 +770,31 @@ export const getWorkspaceTrustedIps = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const addWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
export const addWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
||||||
const { workspaceId } = req.params;
|
|
||||||
const {
|
const {
|
||||||
ipAddress: ip,
|
params: { workspaceId },
|
||||||
comment,
|
body: { comment, isActive, ipAddress: ip }
|
||||||
isActive
|
} = await validateRequest(AddWorkspaceTrustedIpV1, req);
|
||||||
} = req.body;
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionSub.IpAllowList
|
||||||
|
);
|
||||||
|
|
||||||
const plan = await EELicenseService.getPlan(req.workspace.organization);
|
const plan = await EELicenseService.getPlan(req.workspace.organization);
|
||||||
|
|
||||||
if (!plan.ipAllowlisting) return res.status(400).send({
|
if (!plan.ipAllowlisting)
|
||||||
message: "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range."
|
return res.status(400).send({
|
||||||
});
|
message:
|
||||||
|
"Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range."
|
||||||
|
});
|
||||||
|
|
||||||
const isValidIPOrCidr = isValidIpOrCidr(ip);
|
const isValidIPOrCidr = isValidIpOrCidr(ip);
|
||||||
|
|
||||||
if (!isValidIPOrCidr) return res.status(400).send({
|
if (!isValidIPOrCidr)
|
||||||
message: "The IP is not a valid IPv4, IPv6, or CIDR block"
|
return res.status(400).send({
|
||||||
});
|
message: "The IP is not a valid IPv4, IPv6, or CIDR block"
|
||||||
|
});
|
||||||
|
|
||||||
const { ipAddress, type, prefix } = extractIPDetails(ip);
|
const { ipAddress, type, prefix } = extractIPDetails(ip);
|
||||||
|
|
||||||
@@ -743,7 +804,7 @@ export const addWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
|||||||
type,
|
type,
|
||||||
prefix,
|
prefix,
|
||||||
isActive,
|
isActive,
|
||||||
comment,
|
comment
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
@@ -764,7 +825,7 @@ export const addWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
|||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
trustedIp
|
trustedIp
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update trusted ip with id [trustedIpId] workspace with id [workspaceId]
|
* Update trusted ip with id [trustedIpId] workspace with id [workspaceId]
|
||||||
@@ -772,23 +833,31 @@ export const addWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
||||||
const { workspaceId, trustedIpId } = req.params;
|
|
||||||
const {
|
const {
|
||||||
ipAddress: ip,
|
params: { workspaceId, trustedIpId },
|
||||||
comment
|
body: { ipAddress: ip, comment }
|
||||||
} = req.body;
|
} = await validateRequest(UpdateWorkspaceTrustedIpV1, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionSub.IpAllowList
|
||||||
|
);
|
||||||
|
|
||||||
const plan = await EELicenseService.getPlan(req.workspace.organization);
|
const plan = await EELicenseService.getPlan(req.workspace.organization);
|
||||||
|
|
||||||
if (!plan.ipAllowlisting) return res.status(400).send({
|
if (!plan.ipAllowlisting)
|
||||||
message: "Failed to update IP access range due to plan restriction. Upgrade plan to update IP access range."
|
return res.status(400).send({
|
||||||
});
|
message:
|
||||||
|
"Failed to update IP access range due to plan restriction. Upgrade plan to update IP access range."
|
||||||
|
});
|
||||||
|
|
||||||
const isValidIPOrCidr = isValidIpOrCidr(ip);
|
const isValidIPOrCidr = isValidIpOrCidr(ip);
|
||||||
|
|
||||||
if (!isValidIPOrCidr) return res.status(400).send({
|
if (!isValidIPOrCidr)
|
||||||
message: "The IP is not a valid IPv4, IPv6, or CIDR block"
|
return res.status(400).send({
|
||||||
});
|
message: "The IP is not a valid IPv4, IPv6, or CIDR block"
|
||||||
|
});
|
||||||
|
|
||||||
const { ipAddress, type, prefix } = extractIPDetails(ip);
|
const { ipAddress, type, prefix } = extractIPDetails(ip);
|
||||||
|
|
||||||
@@ -799,7 +868,7 @@ export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
|||||||
prefix?: number;
|
prefix?: number;
|
||||||
$unset?: {
|
$unset?: {
|
||||||
prefix: number;
|
prefix: number;
|
||||||
}
|
};
|
||||||
} = {
|
} = {
|
||||||
ipAddress,
|
ipAddress,
|
||||||
type,
|
type,
|
||||||
@@ -815,7 +884,7 @@ export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
|||||||
const trustedIp = await TrustedIP.findOneAndUpdate(
|
const trustedIp = await TrustedIP.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
_id: new Types.ObjectId(trustedIpId),
|
_id: new Types.ObjectId(trustedIpId),
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId)
|
||||||
},
|
},
|
||||||
updateObject,
|
updateObject,
|
||||||
{
|
{
|
||||||
@@ -823,9 +892,10 @@ export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!trustedIp) return res.status(400).send({
|
if (!trustedIp)
|
||||||
message: "Failed to update trusted IP"
|
return res.status(400).send({
|
||||||
});
|
message: "Failed to update trusted IP"
|
||||||
|
});
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
req.authData,
|
req.authData,
|
||||||
@@ -845,7 +915,7 @@ export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
|||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
trustedIp
|
trustedIp
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete IP access range from workspace with id [workspaceId]
|
* Delete IP access range from workspace with id [workspaceId]
|
||||||
@@ -853,22 +923,33 @@ export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const deleteWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
export const deleteWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
||||||
const { workspaceId, trustedIpId } = req.params;
|
const {
|
||||||
|
params: { workspaceId, trustedIpId }
|
||||||
|
} = await validateRequest(DeleteWorkspaceTrustedIpV1, req);
|
||||||
|
|
||||||
|
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
ProjectPermissionSub.IpAllowList
|
||||||
|
);
|
||||||
|
|
||||||
const plan = await EELicenseService.getPlan(req.workspace.organization);
|
const plan = await EELicenseService.getPlan(req.workspace.organization);
|
||||||
|
|
||||||
if (!plan.ipAllowlisting) return res.status(400).send({
|
if (!plan.ipAllowlisting)
|
||||||
message: "Failed to delete IP access range due to plan restriction. Upgrade plan to delete IP access range."
|
return res.status(400).send({
|
||||||
});
|
message:
|
||||||
|
"Failed to delete IP access range due to plan restriction. Upgrade plan to delete IP access range."
|
||||||
|
});
|
||||||
|
|
||||||
const trustedIp = await TrustedIP.findOneAndDelete({
|
const trustedIp = await TrustedIP.findOneAndDelete({
|
||||||
_id: new Types.ObjectId(trustedIpId),
|
_id: new Types.ObjectId(trustedIpId),
|
||||||
workspace: new Types.ObjectId(workspaceId)
|
workspace: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!trustedIp) return res.status(400).send({
|
if (!trustedIp)
|
||||||
message: "Failed to delete trusted IP"
|
return res.status(400).send({
|
||||||
});
|
message: "Failed to delete trusted IP"
|
||||||
|
});
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
req.authData,
|
req.authData,
|
||||||
@@ -888,4 +969,4 @@ export const deleteWorkspaceTrustedIp = async (req: Request, res: Response) => {
|
|||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
trustedIp
|
trustedIp
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
|
||||||
validateRequest,
|
|
||||||
} from "../../../middleware";
|
|
||||||
import { param } from "express-validator";
|
|
||||||
import { actionController } from "../../controllers/v1";
|
import { actionController } from "../../controllers/v1";
|
||||||
|
|
||||||
// TODO: put into action controller
|
// TODO: put into action controller
|
||||||
router.get(
|
router.get("/:actionId", actionController.getAction);
|
||||||
"/:actionId",
|
|
||||||
param("actionId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
actionController.getAction
|
|
||||||
);
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -1,21 +1,16 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import { requireAuth, validateRequest } from "../../../middleware";
|
||||||
requireAuth,
|
|
||||||
validateRequest,
|
|
||||||
} from "../../../middleware";
|
|
||||||
import { query } from "express-validator";
|
|
||||||
import { cloudProductsController } from "../../controllers/v1";
|
import { cloudProductsController } from "../../controllers/v1";
|
||||||
import { AuthMode } from "../../../variables";
|
import { AuthMode } from "../../../variables";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/",
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
query("billing-cycle").exists().isIn(["monthly", "yearly"]),
|
validateRequest,
|
||||||
validateRequest,
|
cloudProductsController.getCloudProducts
|
||||||
cloudProductsController.getCloudProducts
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -1,48 +1,25 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import { requireAuth } from "../../../middleware";
|
||||||
requireAuth,
|
|
||||||
requireSecretAuth,
|
|
||||||
validateRequest,
|
|
||||||
} from "../../../middleware";
|
|
||||||
import { body, param, query } from "express-validator";
|
|
||||||
import { secretController } from "../../controllers/v1";
|
import { secretController } from "../../controllers/v1";
|
||||||
import {
|
import {
|
||||||
ADMIN,
|
AuthMode
|
||||||
AuthMode,
|
|
||||||
MEMBER,
|
|
||||||
PERMISSION_READ_SECRETS,
|
|
||||||
PERMISSION_WRITE_SECRETS
|
|
||||||
} from "../../../variables";
|
} from "../../../variables";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:secretId/secret-versions",
|
"/:secretId/secret-versions",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
requireSecretAuth({
|
secretController.getSecretVersions
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
requiredPermissions: [PERMISSION_READ_SECRETS],
|
|
||||||
}),
|
|
||||||
param("secretId").exists().trim(),
|
|
||||||
query("offset").exists().isInt(),
|
|
||||||
query("limit").exists().isInt(),
|
|
||||||
validateRequest,
|
|
||||||
secretController.getSecretVersions
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/:secretId/secret-versions/rollback",
|
"/:secretId/secret-versions/rollback",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
requireSecretAuth({
|
secretController.rollbackSecretVersion
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
requiredPermissions: [PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS],
|
|
||||||
}),
|
|
||||||
param("secretId").exists().trim(),
|
|
||||||
body("version").exists().isInt(),
|
|
||||||
secretController.rollbackSecretVersion
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -1,182 +1,85 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import { requireAuth } from "../../../middleware";
|
||||||
requireAuth,
|
import { AuthMode } from "../../../variables";
|
||||||
requireWorkspaceAuth,
|
|
||||||
validateRequest,
|
|
||||||
} from "../../../middleware";
|
|
||||||
import { body, param, query } from "express-validator";
|
|
||||||
import {
|
|
||||||
ADMIN,
|
|
||||||
AuthMode,
|
|
||||||
MEMBER
|
|
||||||
} from "../../../variables";
|
|
||||||
import { workspaceController } from "../../controllers/v1";
|
import { workspaceController } from "../../controllers/v1";
|
||||||
import { EventType, UserAgentType } from "../../models";
|
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/secret-snapshots",
|
"/:workspaceId/secret-snapshots",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
query("environment").isString().exists().trim(),
|
|
||||||
query("folderId").default("root").isString().trim(),
|
|
||||||
query("offset").exists().isInt(),
|
|
||||||
query("limit").exists().isInt(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.getWorkspaceSecretSnapshots
|
workspaceController.getWorkspaceSecretSnapshots
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/secret-snapshots/count",
|
"/:workspaceId/secret-snapshots/count",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
query("environment").isString().exists().trim(),
|
|
||||||
query("folderId").default("root").isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.getWorkspaceSecretSnapshotsCount
|
workspaceController.getWorkspaceSecretSnapshotsCount
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/:workspaceId/secret-snapshots/rollback",
|
"/:workspaceId/secret-snapshots/rollback",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
body("environment").isString().exists().trim(),
|
|
||||||
query("folderId").default("root").isString().exists().trim(),
|
|
||||||
body("version").exists().isInt(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.rollbackWorkspaceSecretSnapshot
|
workspaceController.rollbackWorkspaceSecretSnapshot
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/logs",
|
"/:workspaceId/logs",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
query("offset").exists().isInt(),
|
|
||||||
query("limit").exists().isInt(),
|
|
||||||
query("sortBy"),
|
|
||||||
query("userId"),
|
|
||||||
query("actionNames"),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.getWorkspaceLogs
|
workspaceController.getWorkspaceLogs
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/audit-logs",
|
"/:workspaceId/audit-logs",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
query("eventType").isString().isIn(Object.values(EventType)).optional({ nullable: true }),
|
|
||||||
query("userAgentType").isString().isIn(Object.values(UserAgentType)).optional({ nullable: true }),
|
|
||||||
query("actor").optional({ nullable: true }),
|
|
||||||
query("startDate").isISO8601().withMessage("Invalid start date format").optional({ nullable: true }),
|
|
||||||
query("endDate").isISO8601().withMessage("Invalid end date format").optional({ nullable: true }),
|
|
||||||
query("offset"),
|
|
||||||
query("limit"),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.getWorkspaceAuditLogs
|
workspaceController.getWorkspaceAuditLogs
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/audit-logs/filters/actors",
|
"/:workspaceId/audit-logs/filters/actors",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.getWorkspaceAuditLogActorFilterOpts
|
workspaceController.getWorkspaceAuditLogActorFilterOpts
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/trusted-ips",
|
"/:workspaceId/trusted-ips",
|
||||||
param("workspaceId").exists().isString().trim(),
|
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
}),
|
||||||
workspaceController.getWorkspaceTrustedIps
|
workspaceController.getWorkspaceTrustedIps
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/:workspaceId/trusted-ips",
|
"/:workspaceId/trusted-ips",
|
||||||
param("workspaceId").exists().isString().trim(),
|
|
||||||
body("ipAddress").exists().isString().trim(),
|
|
||||||
body("comment").default("").isString().trim(),
|
|
||||||
body("isActive").exists().isBoolean(),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
}),
|
||||||
workspaceController.addWorkspaceTrustedIp
|
workspaceController.addWorkspaceTrustedIp
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/:workspaceId/trusted-ips/:trustedIpId",
|
"/:workspaceId/trusted-ips/:trustedIpId",
|
||||||
param("workspaceId").exists().isString().trim(),
|
|
||||||
param("trustedIpId").exists().isString().trim(),
|
|
||||||
body("ipAddress").isString().trim().default(""),
|
|
||||||
body("comment").default("").isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
}),
|
||||||
workspaceController.updateWorkspaceTrustedIp
|
workspaceController.updateWorkspaceTrustedIp
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/:workspaceId/trusted-ips/:trustedIpId",
|
"/:workspaceId/trusted-ips/:trustedIpId",
|
||||||
param("workspaceId").exists().isString().trim(),
|
|
||||||
param("trustedIpId").exists().isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
}),
|
||||||
workspaceController.deleteWorkspaceTrustedIp
|
workspaceController.deleteWorkspaceTrustedIp
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ export const getIntegrationAuthAccessHelper = async ({
|
|||||||
let accessId;
|
let accessId;
|
||||||
let accessToken;
|
let accessToken;
|
||||||
const integrationAuth = await IntegrationAuth.findById(integrationAuthId).select(
|
const integrationAuth = await IntegrationAuth.findById(integrationAuthId).select(
|
||||||
"workspace integration +accessCiphertext +accessIV +accessTag +accessExpiresAt + refreshCiphertext +accessIdCiphertext +accessIdIV +accessIdTag"
|
"workspace integration +accessCiphertext +accessIV +accessTag +accessExpiresAt +refreshCiphertext +refreshIV +refreshTag +accessIdCiphertext +accessIdIV +accessIdTag"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!integrationAuth)
|
if (!integrationAuth)
|
||||||
@@ -209,6 +209,7 @@ export const getIntegrationAuthAccessHelper = async ({
|
|||||||
if (!accessToken) throw InternalServerError();
|
if (!accessToken) throw InternalServerError();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
integrationAuth,
|
||||||
accessId,
|
accessId,
|
||||||
accessToken
|
accessToken
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { body } from "express-validator";
|
|
||||||
import { requireAuth, validateRequest } from "../../middleware";
|
import { requireAuth, validateRequest } from "../../middleware";
|
||||||
import { authController } from "../../controllers/v1";
|
import { authController } from "../../controllers/v1";
|
||||||
import { authLimiter } from "../../helpers/rateLimiter";
|
import { authLimiter } from "../../helpers/rateLimiter";
|
||||||
@@ -12,9 +11,6 @@ router.post(
|
|||||||
// TODO endpoint: deprecate (moved to api/v3/auth/login1)
|
// TODO endpoint: deprecate (moved to api/v3/auth/login1)
|
||||||
"/login1",
|
"/login1",
|
||||||
authLimiter,
|
authLimiter,
|
||||||
body("email").exists().trim().notEmpty().toLowerCase(),
|
|
||||||
body("clientPublicKey").exists().trim().notEmpty(),
|
|
||||||
validateRequest,
|
|
||||||
authController.login1
|
authController.login1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -22,9 +18,6 @@ router.post(
|
|||||||
// TODO endpoint: deprecate (moved to api/v3/auth/login2)
|
// TODO endpoint: deprecate (moved to api/v3/auth/login2)
|
||||||
"/login2",
|
"/login2",
|
||||||
authLimiter,
|
authLimiter,
|
||||||
body("email").exists().trim().notEmpty().toLowerCase(),
|
|
||||||
body("clientProof").exists().trim().notEmpty(),
|
|
||||||
validateRequest,
|
|
||||||
authController.login2
|
authController.login2
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -45,6 +38,8 @@ router.post(
|
|||||||
authController.checkAuth
|
authController.checkAuth
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.get("/common-passwords", authLimiter, authController.getCommonPasswords);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
// TODO endpoint: deprecate (moved to DELETE v2/users/me/sessions)
|
// TODO endpoint: deprecate (moved to DELETE v2/users/me/sessions)
|
||||||
"/sessions",
|
"/sessions",
|
||||||
|
|||||||
@@ -1,41 +1,25 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { body, param } from "express-validator";
|
|
||||||
import {
|
import {
|
||||||
requireAuth,
|
requireAuth
|
||||||
requireBotAuth,
|
|
||||||
requireWorkspaceAuth,
|
|
||||||
validateRequest,
|
|
||||||
} from "../../middleware";
|
} from "../../middleware";
|
||||||
import { botController } from "../../controllers/v1";
|
import { botController } from "../../controllers/v1";
|
||||||
import { ADMIN, AuthMode, MEMBER } from "../../variables";
|
import { AuthMode } from "../../variables";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId",
|
"/:workspaceId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
botController.getBotByWorkspaceId
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim().notEmpty(),
|
|
||||||
validateRequest,
|
|
||||||
botController.getBotByWorkspaceId
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/:botId/active",
|
"/:botId/active",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireBotAuth({
|
botController.setBotActiveState
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
}),
|
|
||||||
body("isActive").exists().isBoolean(),
|
|
||||||
body("botKey"),
|
|
||||||
validateRequest,
|
|
||||||
botController.setBotActiveState
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -1,45 +1,16 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import {
|
||||||
requireAuth,
|
requireAuth
|
||||||
requireIntegrationAuth,
|
|
||||||
requireIntegrationAuthorizationAuth,
|
|
||||||
requireWorkspaceAuth,
|
|
||||||
validateRequest,
|
|
||||||
} from "../../middleware";
|
} from "../../middleware";
|
||||||
import {
|
import { AuthMode } from "../../variables";
|
||||||
ADMIN,
|
|
||||||
AuthMode,
|
|
||||||
MEMBER
|
|
||||||
} from "../../variables";
|
|
||||||
import { body, param } from "express-validator";
|
|
||||||
import { integrationController } from "../../controllers/v1";
|
import { integrationController } from "../../controllers/v1";
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/",
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
requireIntegrationAuthorizationAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
location: "body",
|
|
||||||
}),
|
|
||||||
body("integrationAuthId").exists().isString().trim(),
|
|
||||||
body("isActive").exists().isBoolean(),
|
|
||||||
body("app").optional().isString().trim(),
|
|
||||||
body("appId").optional().isString().trim(),
|
|
||||||
body("secretPath").default("/").isString().trim(),
|
|
||||||
body("sourceEnvironment").trim(),
|
|
||||||
body("targetEnvironment").trim(),
|
|
||||||
body("targetEnvironmentId").trim(),
|
|
||||||
body("targetService").trim(),
|
|
||||||
body("targetServiceId").trim(),
|
|
||||||
body("owner").trim(),
|
|
||||||
body("path").trim(),
|
|
||||||
body("region").trim(),
|
|
||||||
body("metadata").optional().isObject().withMessage("Metadata should be an object"),
|
|
||||||
body("metadata.secretSuffix").optional().isString().withMessage("Suffix should be a string"),
|
|
||||||
validateRequest,
|
|
||||||
integrationController.createIntegration
|
integrationController.createIntegration
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -48,18 +19,6 @@ router.patch(
|
|||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT]
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireIntegrationAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
}),
|
|
||||||
param("integrationId").exists().trim(),
|
|
||||||
body("isActive").exists().isBoolean(),
|
|
||||||
body("app").exists().trim(),
|
|
||||||
body("secretPath").default("/").isString().trim(),
|
|
||||||
body("environment").exists().trim(),
|
|
||||||
body("appId").exists(),
|
|
||||||
body("targetEnvironment").exists(),
|
|
||||||
body("owner").exists(),
|
|
||||||
validateRequest,
|
|
||||||
integrationController.updateIntegration
|
integrationController.updateIntegration
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -68,11 +27,6 @@ router.delete(
|
|||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT]
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireIntegrationAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
}),
|
|
||||||
param("integrationId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
integrationController.deleteIntegration
|
integrationController.deleteIntegration
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -81,13 +35,6 @@ router.post(
|
|||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT]
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "body",
|
|
||||||
}),
|
|
||||||
body("environment").isString().exists().trim(),
|
|
||||||
body("workspaceId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
integrationController.manualSync
|
integrationController.manualSync
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,174 +1,95 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { body, param, query } from "express-validator";
|
import { requireAuth } from "../../middleware";
|
||||||
import {
|
import { AuthMode } from "../../variables";
|
||||||
requireAuth,
|
|
||||||
requireIntegrationAuthorizationAuth,
|
|
||||||
requireWorkspaceAuth,
|
|
||||||
validateRequest,
|
|
||||||
} from "../../middleware";
|
|
||||||
import {
|
|
||||||
ADMIN,
|
|
||||||
AuthMode,
|
|
||||||
MEMBER
|
|
||||||
} from "../../variables";
|
|
||||||
import { integrationAuthController } from "../../controllers/v1";
|
import { integrationAuthController } from "../../controllers/v1";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/integration-options",
|
"/integration-options",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
integrationAuthController.getIntegrationOptions
|
integrationAuthController.getIntegrationOptions
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:integrationAuthId",
|
"/:integrationAuthId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireIntegrationAuthorizationAuth({
|
integrationAuthController.getIntegrationAuth
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
attachAccessToken: false
|
|
||||||
}),
|
|
||||||
param("integrationAuthId"),
|
|
||||||
validateRequest,
|
|
||||||
integrationAuthController.getIntegrationAuth
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/oauth-token",
|
"/oauth-token",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
integrationAuthController.oAuthExchange
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "body",
|
|
||||||
}),
|
|
||||||
body("workspaceId").exists().trim().notEmpty(),
|
|
||||||
body("code").exists().trim().notEmpty(),
|
|
||||||
body("integration").exists().trim().notEmpty(),
|
|
||||||
body("url").optional().isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
integrationAuthController.oAuthExchange
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/access-token",
|
"/access-token",
|
||||||
body("workspaceId").exists().trim().notEmpty(),
|
requireAuth({
|
||||||
body("refreshToken").optional().isString().trim().notEmpty(),
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
body("accessId").optional().isString().trim(),
|
}),
|
||||||
body("accessToken").optional().isString().trim().notEmpty(),
|
integrationAuthController.saveIntegrationToken
|
||||||
body("url").trim(),
|
|
||||||
body("namespace").trim(),
|
|
||||||
body("integration").exists().trim().notEmpty(),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
|
||||||
}),
|
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "body",
|
|
||||||
}),
|
|
||||||
integrationAuthController.saveIntegrationToken
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:integrationAuthId/apps",
|
"/:integrationAuthId/apps",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireIntegrationAuthorizationAuth({
|
integrationAuthController.getIntegrationAuthApps
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
}),
|
|
||||||
param("integrationAuthId"),
|
|
||||||
query("teamId"),
|
|
||||||
query("workspaceSlug"),
|
|
||||||
validateRequest,
|
|
||||||
integrationAuthController.getIntegrationAuthApps
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:integrationAuthId/teams",
|
"/:integrationAuthId/teams",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireIntegrationAuthorizationAuth({
|
integrationAuthController.getIntegrationAuthTeams
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
}),
|
|
||||||
param("integrationAuthId"),
|
|
||||||
validateRequest,
|
|
||||||
integrationAuthController.getIntegrationAuthTeams
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:integrationAuthId/vercel/branches",
|
"/:integrationAuthId/vercel/branches",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireIntegrationAuthorizationAuth({
|
integrationAuthController.getIntegrationAuthVercelBranches
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
}),
|
|
||||||
param("integrationAuthId").exists().isString(),
|
|
||||||
query("appId").exists().isString(),
|
|
||||||
query("teamId").optional().isString(),
|
|
||||||
validateRequest,
|
|
||||||
integrationAuthController.getIntegrationAuthVercelBranches
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:integrationAuthId/railway/environments",
|
"/:integrationAuthId/railway/environments",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireIntegrationAuthorizationAuth({
|
integrationAuthController.getIntegrationAuthRailwayEnvironments
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
}),
|
|
||||||
param("integrationAuthId").exists().isString(),
|
|
||||||
query("appId").exists().isString(),
|
|
||||||
validateRequest,
|
|
||||||
integrationAuthController.getIntegrationAuthRailwayEnvironments
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:integrationAuthId/railway/services",
|
"/:integrationAuthId/railway/services",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireIntegrationAuthorizationAuth({
|
integrationAuthController.getIntegrationAuthRailwayServices
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
}),
|
|
||||||
param("integrationAuthId").exists().isString(),
|
|
||||||
query("appId").exists().isString(),
|
|
||||||
validateRequest,
|
|
||||||
integrationAuthController.getIntegrationAuthRailwayServices
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:integrationAuthId/bitbucket/workspaces",
|
"/:integrationAuthId/bitbucket/workspaces",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireIntegrationAuthorizationAuth({
|
integrationAuthController.getIntegrationAuthBitBucketWorkspaces
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
}),
|
|
||||||
param("integrationAuthId").exists().isString(),
|
|
||||||
validateRequest,
|
|
||||||
integrationAuthController.getIntegrationAuthBitBucketWorkspaces
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:integrationAuthId/northflank/secret-groups",
|
"/:integrationAuthId/northflank/secret-groups",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireIntegrationAuthorizationAuth({
|
integrationAuthController.getIntegrationAuthNorthflankSecretGroups
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
}),
|
|
||||||
param("integrationAuthId").exists().isString(),
|
|
||||||
query("appId").exists().isString(),
|
|
||||||
validateRequest,
|
|
||||||
integrationAuthController.getIntegrationAuthNorthflankSecretGroups
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
@@ -176,27 +97,15 @@ router.get(
|
|||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT],
|
||||||
}),
|
}),
|
||||||
requireIntegrationAuthorizationAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
}),
|
|
||||||
param("integrationAuthId").exists().isString(),
|
|
||||||
query("appId").exists().isString(),
|
|
||||||
validateRequest,
|
|
||||||
integrationAuthController.getIntegrationAuthTeamCityBuildConfigs
|
integrationAuthController.getIntegrationAuthTeamCityBuildConfigs
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/:integrationAuthId",
|
"/:integrationAuthId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireIntegrationAuthorizationAuth({
|
integrationAuthController.deleteIntegrationAuth
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
attachAccessToken: false,
|
|
||||||
}),
|
|
||||||
param("integrationAuthId"),
|
|
||||||
validateRequest,
|
|
||||||
integrationAuthController.deleteIntegrationAuth
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -1,43 +1,26 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import { requireAuth } from "../../middleware";
|
||||||
requireAuth,
|
import { AuthMode } from "../../variables";
|
||||||
requireWorkspaceAuth,
|
|
||||||
validateRequest,
|
|
||||||
} from "../../middleware";
|
|
||||||
import { body, param } from "express-validator";
|
|
||||||
import { ADMIN, AuthMode, MEMBER } from "../../variables";
|
|
||||||
import { keyController } from "../../controllers/v1";
|
import { keyController } from "../../controllers/v1";
|
||||||
|
|
||||||
// TODO endpoint: consider moving these endpoints to be under /workspaces to be more RESTful
|
// TODO endpoint: consider moving these endpoints to be under /workspaces to be more RESTful
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/:workspaceId",
|
"/:workspaceId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
keyController.uploadKey
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
body("key").exists(),
|
|
||||||
validateRequest,
|
|
||||||
keyController.uploadKey
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get( // TODO endpoint: deprecate (note: move frontend to v2/workspace/key or something)
|
router.get(
|
||||||
"/:workspaceId/latest",
|
// TODO endpoint: deprecate (note: move frontend to v2/workspace/key or something)
|
||||||
requireAuth({
|
"/:workspaceId/latest",
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
requireAuth({
|
||||||
}),
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
requireWorkspaceAuth({
|
}),
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
keyController.getLatestKey
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId"),
|
|
||||||
validateRequest,
|
|
||||||
keyController.getLatestKey
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ const router = express.Router();
|
|||||||
import { body, param } from "express-validator";
|
import { body, param } from "express-validator";
|
||||||
import { requireAuth, validateRequest } from "../../middleware";
|
import { requireAuth, validateRequest } from "../../middleware";
|
||||||
import { membershipController } from "../../controllers/v1";
|
import { membershipController } from "../../controllers/v1";
|
||||||
import { membershipController as EEMembershipControllers } from "../../ee/controllers/v1";
|
|
||||||
import { AuthMode } from "../../variables";
|
import { AuthMode } from "../../variables";
|
||||||
|
|
||||||
// note: ALL DEPRECIATED (moved to api/v2/workspace/:workspaceId/memberships/:membershipId)
|
// note: ALL DEPRECIATED (moved to api/v2/workspace/:workspaceId/memberships/:membershipId)
|
||||||
@@ -42,16 +41,4 @@ router.post(
|
|||||||
membershipController.changeMembershipRole
|
membershipController.changeMembershipRole
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
|
||||||
// TODO endpoint: check dashboard
|
|
||||||
"/:membershipId/deny-permissions",
|
|
||||||
requireAuth({
|
|
||||||
acceptedAuthModes: [AuthMode.JWT]
|
|
||||||
}),
|
|
||||||
param("membershipId").isMongoId().exists().trim(),
|
|
||||||
body("permissions").isArray().exists(),
|
|
||||||
validateRequest,
|
|
||||||
EEMembershipControllers.denyMembershipPermissions
|
|
||||||
);
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -1,93 +1,51 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { body } from "express-validator";
|
import { requireAuth, requireSignupAuth } from "../../middleware";
|
||||||
import { requireAuth, requireSignupAuth, validateRequest } from "../../middleware";
|
|
||||||
import { passwordController } from "../../controllers/v1";
|
import { passwordController } from "../../controllers/v1";
|
||||||
import { passwordLimiter } from "../../helpers/rateLimiter";
|
import { passwordLimiter } from "../../helpers/rateLimiter";
|
||||||
import { AuthMode } from "../../variables";
|
import { AuthMode } from "../../variables";
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/srp1",
|
"/srp1",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
body("clientPublicKey").exists().isString().trim().notEmpty(),
|
passwordController.srp1
|
||||||
validateRequest,
|
|
||||||
passwordController.srp1
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/change-password",
|
"/change-password",
|
||||||
passwordLimiter,
|
passwordLimiter,
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
body("clientProof").exists().trim().notEmpty(),
|
passwordController.changePassword
|
||||||
body("protectedKey").exists().isString().trim().notEmpty(),
|
|
||||||
body("protectedKeyIV").exists().isString().trim().notEmpty(),
|
|
||||||
body("protectedKeyTag").exists().isString().trim().notEmpty(),
|
|
||||||
body("encryptedPrivateKey").exists().isString().trim().notEmpty(), // private key encrypted under new pwd
|
|
||||||
body("encryptedPrivateKeyIV").exists().isString().trim().notEmpty(), // new iv for private key
|
|
||||||
body("encryptedPrivateKeyTag").exists().isString().trim().notEmpty(), // new tag for private key
|
|
||||||
body("salt").exists().isString().trim().notEmpty(), // part of new pwd
|
|
||||||
body("verifier").exists().isString().trim().notEmpty(), // part of new pwd
|
|
||||||
validateRequest,
|
|
||||||
passwordController.changePassword
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post("/email/password-reset", passwordLimiter, passwordController.emailPasswordReset);
|
||||||
"/email/password-reset",
|
|
||||||
passwordLimiter,
|
|
||||||
body("email").exists().isString().trim().notEmpty().isEmail(),
|
|
||||||
validateRequest,
|
|
||||||
passwordController.emailPasswordReset
|
|
||||||
);
|
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/email/password-reset-verify",
|
"/email/password-reset-verify",
|
||||||
passwordLimiter,
|
passwordLimiter,
|
||||||
body("email").exists().isString().trim().notEmpty().isEmail(),
|
passwordController.emailPasswordResetVerify
|
||||||
body("code").exists().isString().trim().notEmpty(),
|
|
||||||
validateRequest,
|
|
||||||
passwordController.emailPasswordResetVerify
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/backup-private-key",
|
"/backup-private-key",
|
||||||
passwordLimiter,
|
passwordLimiter,
|
||||||
requireSignupAuth,
|
requireSignupAuth,
|
||||||
passwordController.getBackupPrivateKey
|
passwordController.getBackupPrivateKey
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/backup-private-key",
|
"/backup-private-key",
|
||||||
passwordLimiter,
|
passwordLimiter,
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
body("clientProof").exists().isString().trim().notEmpty(),
|
passwordController.createBackupPrivateKey
|
||||||
body("encryptedPrivateKey").exists().isString().trim().notEmpty(), // (backup) private key encrypted under a strong key
|
|
||||||
body("iv").exists().isString().trim().notEmpty(), // new iv for (backup) private key
|
|
||||||
body("tag").exists().isString().trim().notEmpty(), // new tag for (backup) private key
|
|
||||||
body("salt").exists().isString().trim().notEmpty(), // salt generated from strong key
|
|
||||||
body("verifier").exists().isString().trim().notEmpty(), // salt generated from strong key
|
|
||||||
validateRequest,
|
|
||||||
passwordController.createBackupPrivateKey
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post("/password-reset", requireSignupAuth, passwordController.resetPassword);
|
||||||
"/password-reset",
|
|
||||||
requireSignupAuth,
|
|
||||||
body("protectedKey").exists().isString().trim().notEmpty(),
|
|
||||||
body("protectedKeyIV").exists().isString().trim().notEmpty(),
|
|
||||||
body("protectedKeyTag").exists().isString().trim().notEmpty(),
|
|
||||||
body("encryptedPrivateKey").exists().isString().trim().notEmpty(), // private key encrypted under new pwd
|
|
||||||
body("encryptedPrivateKeyIV").exists().isString().trim().notEmpty(), // new iv for private key
|
|
||||||
body("encryptedPrivateKeyTag").exists().isString().trim().notEmpty(), // new tag for private key
|
|
||||||
body("salt").exists().isString().trim().notEmpty(), // part of new pwd
|
|
||||||
body("verifier").exists().isString().trim().notEmpty(), // part of new pwd
|
|
||||||
validateRequest,
|
|
||||||
passwordController.resetPassword
|
|
||||||
);
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -1,83 +1,46 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
import { body, param, query } from "express-validator";
|
|
||||||
import { secretImportController } from "../../controllers/v1";
|
import { secretImportController } from "../../controllers/v1";
|
||||||
import { requireAuth, requireWorkspaceAuth, validateRequest } from "../../middleware";
|
import { requireAuth } from "../../middleware";
|
||||||
import { ADMIN, AuthMode, MEMBER } from "../../variables";
|
import { AuthMode } from "../../variables";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/",
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "body"
|
|
||||||
}),
|
|
||||||
body("workspaceId").exists().isString().trim().notEmpty(),
|
|
||||||
body("environment").exists().isString().trim().notEmpty(),
|
|
||||||
body("folderId").default("root").isString().trim(),
|
|
||||||
body("secretImport").exists().isObject(),
|
|
||||||
body("secretImport.environment").isString().exists().trim(),
|
|
||||||
body("secretImport.secretPath").isString().exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
secretImportController.createSecretImport
|
secretImportController.createSecretImport
|
||||||
);
|
);
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
"/:id",
|
"/:id",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN]
|
||||||
}),
|
}),
|
||||||
param("id").exists().isString().trim(),
|
|
||||||
body("secretImports").exists().isArray(),
|
|
||||||
body("secretImports.*.environment").isString().exists().trim(),
|
|
||||||
body("secretImports.*.secretPath").isString().exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
secretImportController.updateSecretImport
|
secretImportController.updateSecretImport
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/:id",
|
"/:id",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN]
|
||||||
}),
|
}),
|
||||||
param("id").exists().isString().trim(),
|
|
||||||
body("secretImportPath").isString().exists().trim(),
|
|
||||||
body("secretImportEnv").isString().exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
secretImportController.deleteSecretImport
|
secretImportController.deleteSecretImport
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/",
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "query"
|
|
||||||
}),
|
|
||||||
query("workspaceId").exists().isString().trim().notEmpty(),
|
|
||||||
query("environment").exists().isString().trim().notEmpty(),
|
|
||||||
query("folderId").default("root").isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
secretImportController.getSecretImports
|
secretImportController.getSecretImports
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/secrets",
|
"/secrets",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "query"
|
|
||||||
}),
|
|
||||||
query("workspaceId").exists().isString().trim().notEmpty(),
|
|
||||||
query("environment").exists().isString().trim().notEmpty(),
|
|
||||||
query("folderId").default("root").isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
secretImportController.getAllSecretsFromImport
|
secretImportController.getAllSecretsFromImport
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,66 +1,43 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
import { body, param, query } from "express-validator";
|
|
||||||
import {
|
import {
|
||||||
createFolder,
|
createFolder,
|
||||||
deleteFolder,
|
deleteFolder,
|
||||||
getFolders,
|
getFolders,
|
||||||
updateFolderById
|
updateFolderById
|
||||||
} from "../../controllers/v1/secretsFolderController";
|
} from "../../controllers/v1/secretsFolderController";
|
||||||
import { requireAuth, requireWorkspaceAuth, validateRequest } from "../../middleware";
|
import { requireAuth } from "../../middleware";
|
||||||
import { ADMIN, AuthMode, MEMBER } from "../../variables";
|
import { AuthMode } from "../../variables";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/",
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "body"
|
|
||||||
}),
|
|
||||||
body("workspaceId").exists(),
|
|
||||||
body("environment").exists(),
|
|
||||||
body("folderName").exists(),
|
|
||||||
body("parentFolderId"),
|
|
||||||
validateRequest,
|
|
||||||
createFolder
|
createFolder
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/:folderId",
|
"/:folderId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN]
|
||||||
}),
|
}),
|
||||||
body("workspaceId").exists(),
|
|
||||||
body("environment").exists(),
|
|
||||||
param("folderId").not().isIn(["root"]).exists(),
|
|
||||||
validateRequest,
|
|
||||||
updateFolderById
|
updateFolderById
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/:folderId",
|
"/:folderId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN]
|
||||||
}),
|
}),
|
||||||
body("workspaceId").exists(),
|
|
||||||
body("environment").exists(),
|
|
||||||
param("folderId").not().isIn(["root"]).exists(),
|
|
||||||
validateRequest,
|
|
||||||
deleteFolder
|
deleteFolder
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/",
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN]
|
||||||
}),
|
}),
|
||||||
query("workspaceId").exists().isString().trim(),
|
|
||||||
query("environment").exists().isString().trim(),
|
|
||||||
query("parentFolderId").optional().isString().trim(),
|
|
||||||
query("parentFolderPath").optional().isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
getFolders
|
getFolders
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,21 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { body } from "express-validator";
|
|
||||||
import { validateRequest } from "../../middleware";
|
|
||||||
import { signupController } from "../../controllers/v1";
|
import { signupController } from "../../controllers/v1";
|
||||||
import { authLimiter } from "../../helpers/rateLimiter";
|
import { authLimiter } from "../../helpers/rateLimiter";
|
||||||
|
|
||||||
// TODO: consider moving to users/v3/signup
|
// TODO: consider moving to users/v3/signup
|
||||||
|
|
||||||
router.post( // TODO endpoint: consider moving to v3/users/signup/mail
|
router.post(
|
||||||
"/email/signup",
|
// TODO endpoint: consider moving to v3/users/signup/mail
|
||||||
authLimiter,
|
"/email/signup",
|
||||||
body("email").exists().trim().notEmpty().isEmail(),
|
authLimiter,
|
||||||
validateRequest,
|
signupController.beginEmailSignup
|
||||||
signupController.beginEmailSignup
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/email/verify", // TODO endpoint: consider moving to v3/users/signup/verify
|
"/email/verify", // TODO endpoint: consider moving to v3/users/signup/verify
|
||||||
authLimiter,
|
authLimiter,
|
||||||
body("email").exists().trim().notEmpty().isEmail(),
|
signupController.verifyEmailSignup
|
||||||
body("code").exists().trim().notEmpty(),
|
|
||||||
validateRequest,
|
|
||||||
signupController.verifyEmailSignup
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -1,29 +1,25 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { requireAuth, validateRequest } from "../../middleware";
|
import { requireAuth } from "../../middleware";
|
||||||
import { body, query } from "express-validator";
|
|
||||||
import { userActionController } from "../../controllers/v1";
|
import { userActionController } from "../../controllers/v1";
|
||||||
import { AuthMode } from "../../variables";
|
import { AuthMode } from "../../variables";
|
||||||
|
|
||||||
// note: [userAction] will be deprecated in /v2 in favor of [action]
|
// note: [userAction] will be deprecated in /v2 in favor of [action]
|
||||||
router.post( // TODO endpoint: move this into /users/me
|
router.post(
|
||||||
"/",
|
// TODO endpoint: move this into /users/me
|
||||||
requireAuth({
|
"/",
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
requireAuth({
|
||||||
}),
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
body("action"),
|
}),
|
||||||
validateRequest,
|
userActionController.addUserAction
|
||||||
userActionController.addUserAction
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/",
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
query("action"),
|
userActionController.getUserAction
|
||||||
validateRequest,
|
|
||||||
userActionController.getUserAction
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -1,74 +1,46 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { requireAuth, requireWorkspaceAuth, validateRequest } from "../../middleware";
|
import { requireAuth } from "../../middleware";
|
||||||
import { body, param, query } from "express-validator";
|
import { AuthMode } from "../../variables";
|
||||||
import { ADMIN, AuthMode, MEMBER } from "../../variables";
|
|
||||||
import { webhookController } from "../../controllers/v1";
|
import { webhookController } from "../../controllers/v1";
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/",
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "body",
|
|
||||||
locationEnvironment: "body"
|
|
||||||
}),
|
|
||||||
body("workspaceId").exists().isString().trim(),
|
|
||||||
body("environment").exists().isString().trim(),
|
|
||||||
body("webhookUrl").exists().isString().isURL().trim(),
|
|
||||||
body("webhookSecretKey").isString().trim(),
|
|
||||||
body("secretPath").default("/").isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
webhookController.createWebhook
|
webhookController.createWebhook
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/:webhookId",
|
"/:webhookId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
param("webhookId").exists().isString().trim(),
|
|
||||||
body("isDisabled").default(false).isBoolean(),
|
|
||||||
validateRequest,
|
|
||||||
webhookController.updateWebhook
|
webhookController.updateWebhook
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/:webhookId/test",
|
"/:webhookId/test",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
param("webhookId").exists().isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
webhookController.testWebhook
|
webhookController.testWebhook
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/:webhookId",
|
"/:webhookId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
param("webhookId").exists().isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
webhookController.deleteWebhook
|
webhookController.deleteWebhook
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/",
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "query",
|
|
||||||
locationEnvironment: "query"
|
|
||||||
}),
|
|
||||||
query("workspaceId").exists().isString().trim(),
|
|
||||||
query("environment").optional().isString().trim(),
|
|
||||||
query("secretPath").optional().isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
webhookController.listWebhooks
|
webhookController.listWebhooks
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,163 +1,95 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { body, param } from "express-validator";
|
import { body, param } from "express-validator";
|
||||||
import {
|
import { requireAuth, requireWorkspaceAuth, validateRequest } from "../../middleware";
|
||||||
requireAuth,
|
import { ADMIN, AuthMode, MEMBER } from "../../variables";
|
||||||
requireWorkspaceAuth,
|
|
||||||
validateRequest,
|
|
||||||
} from "../../middleware";
|
|
||||||
import {
|
|
||||||
ADMIN,
|
|
||||||
AuthMode,
|
|
||||||
MEMBER
|
|
||||||
} from "../../variables";
|
|
||||||
import { membershipController, workspaceController } from "../../controllers/v1";
|
import { membershipController, workspaceController } from "../../controllers/v1";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/keys",
|
"/:workspaceId/keys",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
workspaceController.getWorkspacePublicKeys
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.getWorkspacePublicKeys
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/users",
|
"/:workspaceId/users",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
workspaceController.getWorkspaceMemberships
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.getWorkspaceMemberships
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/",
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
workspaceController.getWorkspaces
|
workspaceController.getWorkspaces
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId",
|
"/:workspaceId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
workspaceController.getWorkspace
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.getWorkspace
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/",
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
body("workspaceName").exists().trim().notEmpty(),
|
workspaceController.deleteWorkspace
|
||||||
body("organizationId").exists().trim().notEmpty(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.createWorkspace
|
|
||||||
);
|
|
||||||
|
|
||||||
router.delete(
|
|
||||||
"/:workspaceId",
|
|
||||||
requireAuth({
|
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
|
||||||
}),
|
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.deleteWorkspace
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/:workspaceId/name",
|
"/:workspaceId/name",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
requireWorkspaceAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
locationWorkspaceId: "params",
|
locationWorkspaceId: "params"
|
||||||
}),
|
}),
|
||||||
param("workspaceId").exists().trim(),
|
param("workspaceId").exists().trim(),
|
||||||
body("name").exists().trim().notEmpty(),
|
body("name").exists().trim().notEmpty(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
workspaceController.changeWorkspaceName
|
workspaceController.changeWorkspaceName
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/:workspaceId/invite-signup",
|
"/:workspaceId/invite-signup",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
membershipController.inviteUserToWorkspace
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
body("email").exists().trim().notEmpty(),
|
|
||||||
validateRequest,
|
|
||||||
membershipController.inviteUserToWorkspace
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/integrations",
|
"/:workspaceId/integrations",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
workspaceController.getWorkspaceIntegrations
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.getWorkspaceIntegrations
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/authorizations",
|
"/:workspaceId/authorizations",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
workspaceController.getWorkspaceIntegrationAuthorizations
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.getWorkspaceIntegrationAuthorizations
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/service-tokens", // TODO endpoint: deprecate
|
"/:workspaceId/service-tokens", // TODO endpoint: deprecate
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
workspaceController.getWorkspaceServiceTokens
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.getWorkspaceServiceTokens
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { requireMfaAuth, validateRequest } from "../../middleware";
|
|||||||
import { authController } from "../../controllers/v2";
|
import { authController } from "../../controllers/v2";
|
||||||
import { authLimiter } from "../../helpers/rateLimiter";
|
import { authLimiter } from "../../helpers/rateLimiter";
|
||||||
|
|
||||||
router.post( // TODO: deprecate (moved to api/v3/auth/login1)
|
router.post(
|
||||||
|
// TODO: deprecate (moved to api/v3/auth/login1)
|
||||||
"/login1",
|
"/login1",
|
||||||
authLimiter,
|
authLimiter,
|
||||||
body("email").isString().trim().notEmpty().toLowerCase(),
|
body("email").isString().trim().notEmpty().toLowerCase(),
|
||||||
@@ -14,7 +15,8 @@ router.post( // TODO: deprecate (moved to api/v3/auth/login1)
|
|||||||
authController.login1
|
authController.login1
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post( // TODO: deprecate (moved to api/v3/auth/login1)
|
router.post(
|
||||||
|
// TODO: deprecate (moved to api/v3/auth/login1)
|
||||||
"/login2",
|
"/login2",
|
||||||
authLimiter,
|
authLimiter,
|
||||||
body("email").isString().trim().notEmpty().toLowerCase(),
|
body("email").isString().trim().notEmpty().toLowerCase(),
|
||||||
@@ -23,22 +25,9 @@ router.post( // TODO: deprecate (moved to api/v3/auth/login1)
|
|||||||
authController.login2
|
authController.login2
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
//remove above ones after depreciation
|
||||||
"/mfa/send",
|
router.post("/mfa/send", authLimiter, authController.sendMfaToken);
|
||||||
authLimiter,
|
|
||||||
body("email").isString().trim().notEmpty().isEmail(),
|
|
||||||
validateRequest,
|
|
||||||
authController.sendMfaToken
|
|
||||||
);
|
|
||||||
|
|
||||||
router.post(
|
router.post("/mfa/verify", authLimiter, requireMfaAuth, authController.verifyMfaToken);
|
||||||
"/mfa/verify",
|
|
||||||
authLimiter,
|
|
||||||
requireMfaAuth,
|
|
||||||
body("email").isString().trim().notEmpty(),
|
|
||||||
body("mfaToken").isString().trim().notEmpty(),
|
|
||||||
validateRequest,
|
|
||||||
authController.verifyMfaToken
|
|
||||||
);
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -1,48 +1,22 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { body, param } from "express-validator";
|
|
||||||
import { environmentController } from "../../controllers/v2";
|
import { environmentController } from "../../controllers/v2";
|
||||||
import {
|
import { requireAuth } from "../../middleware";
|
||||||
requireAuth,
|
import { AuthMode } from "../../variables";
|
||||||
requireWorkspaceAuth,
|
|
||||||
validateRequest,
|
|
||||||
} from "../../middleware";
|
|
||||||
import {
|
|
||||||
ADMIN,
|
|
||||||
AuthMode,
|
|
||||||
MEMBER
|
|
||||||
} from "../../variables";
|
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/:workspaceId/environments",
|
"/:workspaceId/environments",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
body("environmentSlug").exists().trim(),
|
|
||||||
body("environmentName").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
environmentController.createWorkspaceEnvironment
|
environmentController.createWorkspaceEnvironment
|
||||||
);
|
);
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
"/:workspaceId/environments",
|
"/:workspaceId/environments",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
body("environmentSlug").exists().trim(),
|
|
||||||
body("environmentName").exists().trim(),
|
|
||||||
body("oldEnvironmentSlug").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
environmentController.renameWorkspaceEnvironment
|
environmentController.renameWorkspaceEnvironment
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -67,29 +41,16 @@ router.patch(
|
|||||||
router.delete(
|
router.delete(
|
||||||
"/:workspaceId/environments",
|
"/:workspaceId/environments",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
body("environmentSlug").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
environmentController.deleteWorkspaceEnvironment
|
environmentController.deleteWorkspaceEnvironment
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/environments",
|
"/:workspaceId/environments",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [MEMBER, ADMIN],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
environmentController.getAllAccessibleEnvironmentsOfWorkspace
|
environmentController.getAllAccessibleEnvironmentsOfWorkspace
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { Types } from "mongoose";
|
|
||||||
import {
|
import {
|
||||||
requireAuth,
|
requireAuth,
|
||||||
requireSecretsAuth,
|
requireSecretsAuth,
|
||||||
requireWorkspaceAuth,
|
requireWorkspaceAuth,
|
||||||
validateRequest
|
validateRequest
|
||||||
} from "../../middleware";
|
} from "../../middleware";
|
||||||
import { validateClientForSecrets } from "../../validation";
|
import { body } from "express-validator";
|
||||||
import { body, query } from "express-validator";
|
|
||||||
import { secretsController } from "../../controllers/v2";
|
import { secretsController } from "../../controllers/v2";
|
||||||
import {
|
import {
|
||||||
ADMIN,
|
ADMIN,
|
||||||
@@ -19,9 +17,9 @@ import {
|
|||||||
SECRET_PERSONAL,
|
SECRET_PERSONAL,
|
||||||
SECRET_SHARED
|
SECRET_SHARED
|
||||||
} from "../../variables";
|
} from "../../variables";
|
||||||
import { BatchSecretRequest } from "../../types/secret";
|
|
||||||
|
|
||||||
router.post( // TODO endpoint: strongly consider deprecation in favor of a single operation experience on dashboard
|
router.post(
|
||||||
|
// TODO endpoint: strongly consider deprecation in favor of a single operation experience on dashboard
|
||||||
"/batch",
|
"/batch",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
||||||
@@ -30,33 +28,11 @@ router.post( // TODO endpoint: strongly consider deprecation in favor of a singl
|
|||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
locationWorkspaceId: "body"
|
locationWorkspaceId: "body"
|
||||||
}),
|
}),
|
||||||
body("workspaceId").exists().isString().trim(),
|
|
||||||
body("folderId").default("root").isString().trim(),
|
|
||||||
body("environment").exists().isString().trim(),
|
|
||||||
body("secretPath").optional().isString().trim(),
|
|
||||||
body("requests")
|
|
||||||
.exists()
|
|
||||||
.custom(async (requests: BatchSecretRequest[], { req }) => {
|
|
||||||
if (Array.isArray(requests)) {
|
|
||||||
const secretIds = requests
|
|
||||||
.map((request) => request.secret._id)
|
|
||||||
.filter((secretId) => secretId !== undefined);
|
|
||||||
|
|
||||||
if (secretIds.length > 0) {
|
|
||||||
req.secrets = await validateClientForSecrets({
|
|
||||||
authData: req.authData,
|
|
||||||
secretIds: secretIds.map((secretId: string) => new Types.ObjectId(secretId)),
|
|
||||||
requiredPermissions: []
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}),
|
|
||||||
validateRequest,
|
|
||||||
secretsController.batchSecrets
|
secretsController.batchSecrets
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post( // TODO endpoint: deprecate (moved to POST api/v3/secrets)
|
router.post(
|
||||||
|
// TODO endpoint: deprecate (moved to POST api/v3/secrets)
|
||||||
"/",
|
"/",
|
||||||
body("workspaceId").exists().isString().trim(),
|
body("workspaceId").exists().isString().trim(),
|
||||||
body("environment").exists().isString().trim(),
|
body("environment").exists().isString().trim(),
|
||||||
@@ -117,15 +93,9 @@ router.post( // TODO endpoint: deprecate (moved to POST api/v3/secrets)
|
|||||||
secretsController.createSecrets
|
secretsController.createSecrets
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get( // TODO endpoint: deprecate (moved to GET api/v3/secrets)
|
router.get(
|
||||||
|
// TODO endpoint: deprecate (moved to GET api/v3/secrets)
|
||||||
"/",
|
"/",
|
||||||
query("workspaceId").exists().trim(),
|
|
||||||
query("environment").exists().trim(),
|
|
||||||
query("tagSlugs"),
|
|
||||||
query("folderId").default("root").isString().trim(),
|
|
||||||
query("secretPath").optional().isString().trim(),
|
|
||||||
query("include_imports").optional().default(false).isBoolean(),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
||||||
}),
|
}),
|
||||||
@@ -138,7 +108,8 @@ router.get( // TODO endpoint: deprecate (moved to GET api/v3/secrets)
|
|||||||
secretsController.getSecrets
|
secretsController.getSecrets
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch( // TODO endpoint: deprecate (moved to PATCH api/v3/secrets)
|
router.patch(
|
||||||
|
// TODO endpoint: deprecate (moved to PATCH api/v3/secrets)
|
||||||
"/",
|
"/",
|
||||||
body("secrets")
|
body("secrets")
|
||||||
.exists()
|
.exists()
|
||||||
@@ -173,7 +144,8 @@ router.patch( // TODO endpoint: deprecate (moved to PATCH api/v3/secrets)
|
|||||||
secretsController.updateSecrets
|
secretsController.updateSecrets
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete( // TODO endpoint: deprecate (moved to DELETE api/v3/secrets)
|
router.delete(
|
||||||
|
// TODO endpoint: deprecate (moved to DELETE api/v3/secrets)
|
||||||
"/",
|
"/",
|
||||||
body("secretIds")
|
body("secretIds")
|
||||||
.exists()
|
.exists()
|
||||||
|
|||||||
@@ -1,18 +1,9 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import {
|
||||||
requireAuth,
|
requireAuth
|
||||||
requireServiceTokenDataAuth,
|
|
||||||
requireWorkspaceAuth,
|
|
||||||
validateRequest
|
|
||||||
} from "../../middleware";
|
} from "../../middleware";
|
||||||
import { body, param } from "express-validator";
|
import { AuthMode } from "../../variables";
|
||||||
import {
|
|
||||||
ADMIN,
|
|
||||||
AuthMode,
|
|
||||||
MEMBER,
|
|
||||||
PERMISSION_WRITE_SECRETS
|
|
||||||
} from "../../variables";
|
|
||||||
import { serviceTokenDataController } from "../../controllers/v2";
|
import { serviceTokenDataController } from "../../controllers/v2";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
@@ -28,33 +19,6 @@ router.post(
|
|||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT]
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "body",
|
|
||||||
locationEnvironment: "body",
|
|
||||||
requiredPermissions: [PERMISSION_WRITE_SECRETS]
|
|
||||||
}),
|
|
||||||
body("name").exists().isString().trim(),
|
|
||||||
body("workspaceId").exists().isString().trim(),
|
|
||||||
body("scopes").exists().isArray(),
|
|
||||||
body("scopes.*.environment").exists().isString().trim(),
|
|
||||||
body("scopes.*.secretPath").exists().isString().trim(),
|
|
||||||
body("encryptedKey").exists().isString().trim(),
|
|
||||||
body("iv").exists().isString().trim(),
|
|
||||||
body("tag").exists().isString().trim(),
|
|
||||||
body("expiresIn").exists().isNumeric(), // measured in ms
|
|
||||||
body("permissions")
|
|
||||||
.isArray({ min: 1 })
|
|
||||||
.custom((value: string[]) => {
|
|
||||||
const allowedPermissions = ["read", "write"];
|
|
||||||
const invalidValues = value.filter((v) => !allowedPermissions.includes(v));
|
|
||||||
if (invalidValues.length > 0) {
|
|
||||||
throw new Error(`permissions contains invalid values: ${invalidValues.join(", ")}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}),
|
|
||||||
validateRequest,
|
|
||||||
serviceTokenDataController.createServiceTokenData
|
serviceTokenDataController.createServiceTokenData
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -63,11 +27,6 @@ router.delete(
|
|||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT]
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireServiceTokenDataAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER]
|
|
||||||
}),
|
|
||||||
param("serviceTokenDataId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
serviceTokenDataController.deleteServiceTokenData
|
serviceTokenDataController.deleteServiceTokenData
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,56 +1,30 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { body, param } from "express-validator";
|
|
||||||
import { tagController } from "../../controllers/v2";
|
import { tagController } from "../../controllers/v2";
|
||||||
import {
|
import { requireAuth } from "../../middleware";
|
||||||
requireAuth,
|
import { AuthMode } from "../../variables";
|
||||||
requireWorkspaceAuth,
|
|
||||||
validateRequest,
|
|
||||||
} from "../../middleware";
|
|
||||||
import {
|
|
||||||
ADMIN,
|
|
||||||
AuthMode,
|
|
||||||
MEMBER
|
|
||||||
} from "../../variables";
|
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/tags",
|
"/:workspaceId/tags",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [MEMBER, ADMIN],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
tagController.getWorkspaceTags
|
tagController.getWorkspaceTags
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/tags/:tagId",
|
"/tags/:tagId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
param("tagId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
tagController.deleteWorkspaceTag
|
tagController.deleteWorkspaceTag
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/:workspaceId/tags",
|
"/:workspaceId/tags",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [MEMBER, ADMIN],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
body("name").exists().trim(),
|
|
||||||
body("tagColor").exists().trim(),
|
|
||||||
body("slug").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
tagController.createWorkspaceTag
|
tagController.createWorkspaceTag
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,114 +1,87 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import { requireAuth } from "../../middleware";
|
||||||
requireAuth,
|
|
||||||
validateRequest,
|
|
||||||
} from "../../middleware";
|
|
||||||
import { body, param } from "express-validator";
|
|
||||||
import { usersController } from "../../controllers/v2";
|
import { usersController } from "../../controllers/v2";
|
||||||
import { AuthMode } from "../../variables";
|
import { AuthMode } from "../../variables";
|
||||||
import {
|
|
||||||
AuthMethod
|
|
||||||
} from "../../models";
|
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/me",
|
"/me",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
usersController.getMe
|
usersController.getMe
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/me/mfa",
|
"/me/mfa",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
body("isMfaEnabled").exists().isBoolean(),
|
usersController.updateMyMfaEnabled
|
||||||
validateRequest,
|
|
||||||
usersController.updateMyMfaEnabled
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/me/name",
|
"/me/name",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
body("firstName").exists().isString(),
|
usersController.updateName
|
||||||
body("lastName").isString(),
|
|
||||||
validateRequest,
|
|
||||||
usersController.updateName
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
"/me/auth-methods",
|
"/me/auth-methods",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
body("authMethods").exists().isArray({
|
usersController.updateAuthMethods
|
||||||
min: 1,
|
|
||||||
}).custom((authMethods: AuthMethod[]) => {
|
|
||||||
return authMethods.every(provider => [
|
|
||||||
AuthMethod.EMAIL,
|
|
||||||
AuthMethod.GOOGLE,
|
|
||||||
AuthMethod.GITHUB
|
|
||||||
].includes(provider))
|
|
||||||
}),
|
|
||||||
validateRequest,
|
|
||||||
usersController.updateAuthMethods,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/me/organizations",
|
"/me/organizations",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
}),
|
}),
|
||||||
usersController.getMyOrganizations
|
usersController.getMyOrganizations
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/me/api-keys",
|
"/me/api-keys",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT]
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
usersController.getMyAPIKeys
|
usersController.getMyAPIKeys
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/me/api-keys",
|
"/me/api-keys",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT]
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
body("name").exists().isString().trim(),
|
usersController.createAPIKey
|
||||||
body("expiresIn").isNumeric(),
|
|
||||||
validateRequest,
|
|
||||||
usersController.createAPIKey
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/me/api-keys/:apiKeyDataId",
|
"/me/api-keys/:apiKeyDataId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT]
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
param("apiKeyDataId").exists().trim(),
|
usersController.deleteAPIKey
|
||||||
validateRequest,
|
|
||||||
usersController.deleteAPIKey
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/me/sessions",
|
"/me/sessions",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT]
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
usersController.getMySessions
|
usersController.getMySessions
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/me/sessions",
|
"/me/sessions",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AuthMode.JWT]
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
}),
|
}),
|
||||||
usersController.deleteMySessions
|
usersController.deleteMySessions
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -1,147 +1,96 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { body, param, query } from "express-validator";
|
import { body, param, query } from "express-validator";
|
||||||
import {
|
import { requireAuth, requireWorkspaceAuth, validateRequest } from "../../middleware";
|
||||||
requireAuth,
|
import { ADMIN, AuthMode, MEMBER } from "../../variables";
|
||||||
requireMembershipAuth,
|
|
||||||
requireWorkspaceAuth,
|
|
||||||
validateRequest,
|
|
||||||
} from "../../middleware";
|
|
||||||
import {
|
|
||||||
ADMIN,
|
|
||||||
AuthMode,
|
|
||||||
MEMBER
|
|
||||||
} from "../../variables";
|
|
||||||
import { workspaceController } from "../../controllers/v2";
|
import { workspaceController } from "../../controllers/v2";
|
||||||
|
|
||||||
router.post( // TODO endpoint: deprecate (moved to POST v3/secrets)
|
router.post(
|
||||||
"/:workspaceId/secrets",
|
// TODO endpoint: deprecate (moved to POST v3/secrets)
|
||||||
requireAuth({
|
"/:workspaceId/secrets",
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
requireAuth({
|
||||||
}),
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
requireWorkspaceAuth({
|
}),
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
requireWorkspaceAuth({
|
||||||
locationWorkspaceId: "params",
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
}),
|
locationWorkspaceId: "params"
|
||||||
body("secrets").exists(),
|
}),
|
||||||
body("keys").exists(),
|
body("secrets").exists(),
|
||||||
body("environment").exists().trim().notEmpty(),
|
body("keys").exists(),
|
||||||
body("channel"),
|
body("environment").exists().trim().notEmpty(),
|
||||||
param("workspaceId").exists().trim(),
|
body("channel"),
|
||||||
validateRequest,
|
param("workspaceId").exists().trim(),
|
||||||
workspaceController.pushWorkspaceSecrets
|
validateRequest,
|
||||||
);
|
workspaceController.pushWorkspaceSecrets
|
||||||
|
|
||||||
router.get( // TODO endpoint: deprecate (moved to GET v3/secrets)
|
|
||||||
"/:workspaceId/secrets",
|
|
||||||
requireAuth({
|
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN],
|
|
||||||
}),
|
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
query("environment").exists().trim(),
|
|
||||||
query("channel"),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.pullSecrets
|
|
||||||
);
|
|
||||||
|
|
||||||
router.get( // TODO endpoint: consider moving to v3/users/me/workspaces/:workspaceId/key
|
|
||||||
"/:workspaceId/encrypted-key",
|
|
||||||
requireAuth({
|
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
|
||||||
}),
|
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
|
||||||
validateRequest,
|
|
||||||
workspaceController.getWorkspaceKey
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/service-token-data",
|
// TODO endpoint: deprecate (moved to GET v3/secrets)
|
||||||
requireAuth({
|
"/:workspaceId/secrets",
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
requireAuth({
|
||||||
}),
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN]
|
||||||
requireWorkspaceAuth({
|
}),
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
requireWorkspaceAuth({
|
||||||
locationWorkspaceId: "params",
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
}),
|
locationWorkspaceId: "params"
|
||||||
param("workspaceId").exists().trim(),
|
}),
|
||||||
validateRequest,
|
query("environment").exists().trim(),
|
||||||
workspaceController.getWorkspaceServiceTokenData
|
query("channel"),
|
||||||
|
param("workspaceId").exists().trim(),
|
||||||
|
validateRequest,
|
||||||
|
workspaceController.pullSecrets
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get( // new - TODO: rewire dashboard to this route
|
router.get(
|
||||||
"/:workspaceId/memberships",
|
// TODO endpoint: consider moving to v3/users/me/workspaces/:workspaceId/key
|
||||||
param("workspaceId").exists().trim(),
|
"/:workspaceId/encrypted-key",
|
||||||
validateRequest,
|
requireAuth({
|
||||||
requireAuth({
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
}),
|
||||||
}),
|
workspaceController.getWorkspaceKey
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
workspaceController.getWorkspaceMemberships
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch( // TODO - rewire dashboard to this route
|
router.get(
|
||||||
"/:workspaceId/memberships/:membershipId",
|
"/:workspaceId/service-token-data",
|
||||||
param("workspaceId").exists().trim(),
|
requireAuth({
|
||||||
param("membershipId").exists().trim(),
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
body("role").exists().isString().trim().isIn([ADMIN, MEMBER]),
|
}),
|
||||||
validateRequest,
|
workspaceController.getWorkspaceServiceTokenData
|
||||||
requireAuth({
|
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
|
||||||
}),
|
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
requireMembershipAuth({
|
|
||||||
acceptedRoles: [ADMIN],
|
|
||||||
locationMembershipId: "params",
|
|
||||||
}),
|
|
||||||
workspaceController.updateWorkspaceMembership
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete( // TODO - rewire dashboard to this route
|
router.get(
|
||||||
"/:workspaceId/memberships/:membershipId",
|
// new - TODO: rewire dashboard to this route
|
||||||
param("workspaceId").exists().trim(),
|
"/:workspaceId/memberships",
|
||||||
param("membershipId").exists().trim(),
|
requireAuth({
|
||||||
validateRequest,
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
requireAuth({
|
}),
|
||||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
|
workspaceController.getWorkspaceMemberships
|
||||||
}),
|
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
requireMembershipAuth({
|
|
||||||
acceptedRoles: [ADMIN],
|
|
||||||
locationMembershipId: "params",
|
|
||||||
}),
|
|
||||||
workspaceController.deleteWorkspaceMembership
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/:workspaceId/auto-capitalization",
|
// TODO - rewire dashboard to this route
|
||||||
requireAuth({
|
"/:workspaceId/memberships/:membershipId",
|
||||||
acceptedAuthModes: [AuthMode.JWT]
|
requireAuth({
|
||||||
}),
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
requireWorkspaceAuth({
|
}),
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
workspaceController.updateWorkspaceMembership
|
||||||
locationWorkspaceId: "params",
|
);
|
||||||
}),
|
|
||||||
param("workspaceId").exists().trim(),
|
router.delete(
|
||||||
body("autoCapitalization").exists().trim().notEmpty(),
|
// TODO - rewire dashboard to this route
|
||||||
validateRequest,
|
"/:workspaceId/memberships/:membershipId",
|
||||||
workspaceController.toggleAutoCapitalization
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||||
|
}),
|
||||||
|
workspaceController.deleteWorkspaceMembership
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/:workspaceId/auto-capitalization",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
|
}),
|
||||||
|
workspaceController.toggleAutoCapitalization
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -1,29 +1,11 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
import { body } from "express-validator";
|
|
||||||
import { validateRequest } from "../../middleware";
|
|
||||||
import { authController } from "../../controllers/v3";
|
import { authController } from "../../controllers/v3";
|
||||||
import { authLimiter } from "../../helpers/rateLimiter";
|
import { authLimiter } from "../../helpers/rateLimiter";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.post(
|
router.post("/login1", authLimiter, authController.login1);
|
||||||
"/login1",
|
|
||||||
authLimiter,
|
|
||||||
body("email").isString().trim().toLowerCase(),
|
|
||||||
body("providerAuthToken").isString().trim().optional({nullable: true}),
|
|
||||||
body("clientPublicKey").isString().trim().notEmpty(),
|
|
||||||
validateRequest,
|
|
||||||
authController.login1
|
|
||||||
);
|
|
||||||
|
|
||||||
router.post(
|
router.post("/login2", authLimiter, authController.login2);
|
||||||
"/login2",
|
|
||||||
authLimiter,
|
|
||||||
body("email").isString().trim().toLowerCase(),
|
|
||||||
body("providerAuthToken").isString().trim().optional({nullable: true}),
|
|
||||||
body("clientProof").isString().trim().notEmpty(),
|
|
||||||
validateRequest,
|
|
||||||
authController.login2
|
|
||||||
);
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -15,36 +15,18 @@ import {
|
|||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/raw",
|
"/raw",
|
||||||
query("workspaceId").optional().isString().trim(),
|
|
||||||
query("environment").optional().isString().trim(),
|
|
||||||
query("folderId").optional().isString().trim(),
|
|
||||||
query("secretPath").default("/").isString().trim(),
|
|
||||||
query("include_imports").optional().isBoolean().default(false),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
||||||
AuthMode.JWT,
|
|
||||||
AuthMode.API_KEY,
|
|
||||||
AuthMode.SERVICE_TOKEN
|
|
||||||
]
|
|
||||||
}),
|
}),
|
||||||
secretsController.getSecretsRaw
|
secretsController.getSecretsRaw
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO(akhilmhdh): tony please split the requireWorkspaceAuth to multiple middlewares
|
||||||
|
// IP checking into another one
|
||||||
router.get(
|
router.get(
|
||||||
"/raw/:secretName",
|
"/raw/:secretName",
|
||||||
param("secretName").exists().isString().trim(),
|
|
||||||
query("workspaceId").exists().isString().trim(),
|
|
||||||
query("environment").exists().isString().trim(),
|
|
||||||
query("secretPath").default("/").isString().trim(),
|
|
||||||
query("type").optional().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
||||||
AuthMode.JWT,
|
|
||||||
AuthMode.API_KEY,
|
|
||||||
AuthMode.SERVICE_TOKEN
|
|
||||||
]
|
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
requireWorkspaceAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
@@ -60,19 +42,8 @@ router.get(
|
|||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/raw/:secretName",
|
"/raw/:secretName",
|
||||||
body("workspaceId").exists().isString().trim(),
|
|
||||||
body("environment").exists().isString().trim(),
|
|
||||||
body("type").exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
|
||||||
body("secretValue").exists().isString().trim(),
|
|
||||||
body("secretComment").default("").isString().trim(),
|
|
||||||
body("secretPath").default("/").isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
||||||
AuthMode.JWT,
|
|
||||||
AuthMode.API_KEY,
|
|
||||||
AuthMode.SERVICE_TOKEN
|
|
||||||
]
|
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
requireWorkspaceAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
@@ -88,19 +59,8 @@ router.post(
|
|||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/raw/:secretName",
|
"/raw/:secretName",
|
||||||
param("secretName").exists().isString().trim(),
|
|
||||||
body("workspaceId").exists().isString().trim(),
|
|
||||||
body("environment").exists().isString().trim(),
|
|
||||||
body("type").exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
|
||||||
body("secretValue").exists().isString().trim(),
|
|
||||||
body("secretPath").default("/").isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
||||||
AuthMode.JWT,
|
|
||||||
AuthMode.API_KEY,
|
|
||||||
AuthMode.SERVICE_TOKEN
|
|
||||||
]
|
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
requireWorkspaceAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
@@ -116,18 +76,8 @@ router.patch(
|
|||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/raw/:secretName",
|
"/raw/:secretName",
|
||||||
param("secretName").exists().isString().trim(),
|
|
||||||
body("workspaceId").exists().isString().trim(),
|
|
||||||
body("environment").exists().isString().trim(),
|
|
||||||
body("secretPath").default("/").isString().trim(),
|
|
||||||
body("type").exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
||||||
AuthMode.JWT,
|
|
||||||
AuthMode.API_KEY,
|
|
||||||
AuthMode.SERVICE_TOKEN
|
|
||||||
]
|
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
requireWorkspaceAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
@@ -149,11 +99,7 @@ router.get(
|
|||||||
query("secretPath").default("/").isString().trim(),
|
query("secretPath").default("/").isString().trim(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
||||||
AuthMode.JWT,
|
|
||||||
AuthMode.API_KEY,
|
|
||||||
AuthMode.SERVICE_TOKEN
|
|
||||||
]
|
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
requireWorkspaceAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
@@ -169,28 +115,8 @@ router.get(
|
|||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/:secretName",
|
"/:secretName",
|
||||||
body("workspaceId").exists().isString().trim(),
|
|
||||||
body("environment").exists().isString().trim(),
|
|
||||||
body("type").exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
|
||||||
body("secretKeyCiphertext").exists().isString().trim(),
|
|
||||||
body("secretKeyIV").exists().isString().trim(),
|
|
||||||
body("secretKeyTag").exists().isString().trim(),
|
|
||||||
body("secretValueCiphertext").exists().isString().trim(),
|
|
||||||
body("secretValueIV").exists().isString().trim(),
|
|
||||||
body("secretValueTag").exists().isString().trim(),
|
|
||||||
body("secretCommentCiphertext").optional().isString().trim(),
|
|
||||||
body("secretCommentIV").optional().isString().trim(),
|
|
||||||
body("secretCommentTag").optional().isString().trim(),
|
|
||||||
body("secretPath").default("/").isString().trim(),
|
|
||||||
body("metadata").optional().isObject().withMessage("Metadata should be an object"),
|
|
||||||
body("metadata.source").optional().isString().withMessage("Source should be a string"),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
||||||
AuthMode.JWT,
|
|
||||||
AuthMode.API_KEY,
|
|
||||||
AuthMode.SERVICE_TOKEN
|
|
||||||
]
|
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
requireWorkspaceAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
@@ -206,18 +132,8 @@ router.post(
|
|||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:secretName",
|
"/:secretName",
|
||||||
param("secretName").exists().isString().trim(),
|
|
||||||
query("workspaceId").exists().isString().trim(),
|
|
||||||
query("environment").exists().isString().trim(),
|
|
||||||
query("secretPath").default("/").isString().trim(),
|
|
||||||
query("type").optional().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
||||||
AuthMode.JWT,
|
|
||||||
AuthMode.API_KEY,
|
|
||||||
AuthMode.SERVICE_TOKEN
|
|
||||||
]
|
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
requireWorkspaceAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
@@ -232,21 +148,8 @@ router.get(
|
|||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/:secretName",
|
"/:secretName",
|
||||||
param("secretName").exists().isString().trim(),
|
|
||||||
body("workspaceId").exists().isString().trim(),
|
|
||||||
body("environment").exists().isString().trim(),
|
|
||||||
body("type").exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
|
||||||
body("secretValueCiphertext").exists().isString().trim(),
|
|
||||||
body("secretValueIV").exists().isString().trim(),
|
|
||||||
body("secretValueTag").exists().isString().trim(),
|
|
||||||
body("secretPath").default("/").isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
||||||
AuthMode.JWT,
|
|
||||||
AuthMode.API_KEY,
|
|
||||||
AuthMode.SERVICE_TOKEN
|
|
||||||
]
|
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
requireWorkspaceAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
@@ -269,11 +172,7 @@ router.delete(
|
|||||||
body("type").exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
body("type").exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [
|
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
|
||||||
AuthMode.JWT,
|
|
||||||
AuthMode.API_KEY,
|
|
||||||
AuthMode.SERVICE_TOKEN
|
|
||||||
]
|
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
requireWorkspaceAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
|
|||||||
@@ -1,30 +1,14 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { body } from "express-validator";
|
|
||||||
import { signupController } from "../../controllers/v3";
|
import { signupController } from "../../controllers/v3";
|
||||||
import { authLimiter } from "../../helpers/rateLimiter";
|
import { authLimiter } from "../../helpers/rateLimiter";
|
||||||
import { validateRequest } from "../../middleware";
|
import { validateRequest } from "../../middleware";
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/complete-account/signup", // TODO: consider moving endpoint to v3/users/new/complete-account/signup
|
"/complete-account/signup", // TODO: consider moving endpoint to v3/users/new/complete-account/signup
|
||||||
authLimiter,
|
authLimiter,
|
||||||
body("email").exists().isString().trim().notEmpty().isEmail(),
|
validateRequest,
|
||||||
body("firstName").exists().isString().trim().notEmpty(),
|
signupController.completeAccountSignup
|
||||||
body("lastName").exists().isString().trim().optional({nullable: true}),
|
|
||||||
body("protectedKey").exists().isString().trim().notEmpty(),
|
|
||||||
body("protectedKeyIV").exists().isString().trim().notEmpty(),
|
|
||||||
body("protectedKeyTag").exists().isString().trim().notEmpty(),
|
|
||||||
body("publicKey").exists().isString().trim().notEmpty(),
|
|
||||||
body("encryptedPrivateKey").exists().isString().trim().notEmpty(),
|
|
||||||
body("encryptedPrivateKeyIV").exists().isString().trim().notEmpty(),
|
|
||||||
body("encryptedPrivateKeyTag").exists().isString().trim().notEmpty(),
|
|
||||||
body("salt").exists().isString().trim().notEmpty(),
|
|
||||||
body("verifier").exists().isString().trim().notEmpty(),
|
|
||||||
body("organizationName").exists().isString().trim().notEmpty(),
|
|
||||||
body("providerAuthToken").isString().trim().optional({ nullable: true }),
|
|
||||||
body("attributionSource").optional().isString().trim(),
|
|
||||||
validateRequest,
|
|
||||||
signupController.completeAccountSignup,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -1,77 +1,35 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import { requireAuth } from "../../middleware";
|
||||||
requireAuth,
|
|
||||||
requireWorkspaceAuth,
|
|
||||||
validateRequest,
|
|
||||||
} from "../../middleware";
|
|
||||||
import { workspacesController } from "../../controllers/v3";
|
import { workspacesController } from "../../controllers/v3";
|
||||||
import {
|
import { AuthMode } from "../../variables";
|
||||||
ADMIN,
|
|
||||||
AuthMode
|
|
||||||
} from "../../variables";
|
|
||||||
import { body, param } from "express-validator";
|
|
||||||
|
|
||||||
// -- migration to blind indices endpoints
|
// -- migration to blind indices endpoints
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/:workspaceId/secrets/blind-index-status",
|
"/:workspaceId/secrets/blind-index-status",
|
||||||
param("workspaceId").exists().isString().trim(),
|
requireAuth({
|
||||||
validateRequest,
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
requireAuth({
|
}),
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
workspacesController.getWorkspaceBlindIndexStatus
|
||||||
}),
|
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
workspacesController.getWorkspaceBlindIndexStatus
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get( // allow admins to get all workspace secrets (part of blind indices migration)
|
router.get(
|
||||||
"/:workspaceId/secrets",
|
// allow admins to get all workspace secrets (part of blind indices migration)
|
||||||
param("workspaceId").exists().isString().trim(),
|
"/:workspaceId/secrets",
|
||||||
validateRequest,
|
requireAuth({
|
||||||
requireAuth({
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
}),
|
||||||
}),
|
workspacesController.getWorkspaceSecrets
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
workspacesController.getWorkspaceSecrets
|
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post( // allow admins to name all workspace secrets (part of blind indices migration)
|
router.post(
|
||||||
"/:workspaceId/secrets/names",
|
// allow admins to name all workspace secrets (part of blind indices migration)
|
||||||
param("workspaceId").exists().isString().trim(),
|
"/:workspaceId/secrets/names",
|
||||||
body("secretsToUpdate")
|
requireAuth({
|
||||||
.exists()
|
acceptedAuthModes: [AuthMode.JWT]
|
||||||
.isArray()
|
}),
|
||||||
.withMessage("secretsToUpdate must be an array")
|
workspacesController.nameWorkspaceSecrets
|
||||||
.customSanitizer((value) => {
|
|
||||||
return value.map((secret: any) => ({
|
|
||||||
secretName: secret.secretName,
|
|
||||||
_id: secret._id,
|
|
||||||
}));
|
|
||||||
}),
|
|
||||||
body("secretsToUpdate.*.secretName")
|
|
||||||
.exists()
|
|
||||||
.isString()
|
|
||||||
.withMessage("secretName must be a string"),
|
|
||||||
body("secretsToUpdate.*._id")
|
|
||||||
.exists()
|
|
||||||
.isString()
|
|
||||||
.withMessage("secretId must be a string"),
|
|
||||||
validateRequest,
|
|
||||||
requireAuth({
|
|
||||||
acceptedAuthModes: [AuthMode.JWT],
|
|
||||||
}),
|
|
||||||
requireWorkspaceAuth({
|
|
||||||
acceptedRoles: [ADMIN],
|
|
||||||
locationWorkspaceId: "params",
|
|
||||||
}),
|
|
||||||
workspacesController.nameWorkspaceSecrets
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// --
|
// --
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
import { AbilityBuilder, MongoAbility, RawRuleOf, createMongoAbility } from "@casl/ability";
|
import {
|
||||||
|
AbilityBuilder,
|
||||||
|
ForcedSubject,
|
||||||
|
MongoAbility,
|
||||||
|
RawRuleOf,
|
||||||
|
createMongoAbility
|
||||||
|
} from "@casl/ability";
|
||||||
import { Membership } from "../models";
|
import { Membership } from "../models";
|
||||||
import { IRole } from "../models/role";
|
import { IRole } from "../models/role";
|
||||||
import { BadRequestError, UnauthorizedRequestError } from "../utils/errors";
|
import { BadRequestError, UnauthorizedRequestError } from "../utils/errors";
|
||||||
|
|
||||||
export enum GeneralPermissionActions {
|
export enum ProjectPermissionActions {
|
||||||
Read = "read",
|
Read = "read",
|
||||||
Create = "create",
|
Create = "create",
|
||||||
Edit = "edit",
|
Edit = "edit",
|
||||||
Delete = "delete"
|
Delete = "delete"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ProjectPermission {
|
export enum ProjectPermissionSub {
|
||||||
Role = "role",
|
Role = "role",
|
||||||
Member = "member",
|
Member = "member",
|
||||||
Settings = "settings",
|
Settings = "settings",
|
||||||
@@ -24,96 +30,119 @@ export enum ProjectPermission {
|
|||||||
Workspace = "workspace",
|
Workspace = "workspace",
|
||||||
Secrets = "secrets",
|
Secrets = "secrets",
|
||||||
SecretImports = "secret-imports",
|
SecretImports = "secret-imports",
|
||||||
|
SecretRollback = "secret-rollback",
|
||||||
Folders = "folders"
|
Folders = "folders"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GenericFields = {
|
||||||
|
environment: string;
|
||||||
|
secretPath?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ProjectPermissionSet =
|
export type ProjectPermissionSet =
|
||||||
| [GeneralPermissionActions, ProjectPermission.Secrets]
|
| [
|
||||||
| [GeneralPermissionActions, ProjectPermission.Folders]
|
ProjectPermissionActions,
|
||||||
| [GeneralPermissionActions, ProjectPermission.SecretImports]
|
ProjectPermissionSub.Secrets | (ForcedSubject<ProjectPermissionSub.Secrets> & GenericFields)
|
||||||
| [GeneralPermissionActions, ProjectPermission.Role]
|
]
|
||||||
| [GeneralPermissionActions, ProjectPermission.Tags]
|
| [
|
||||||
| [GeneralPermissionActions, ProjectPermission.Member]
|
ProjectPermissionActions,
|
||||||
| [GeneralPermissionActions, ProjectPermission.Integrations]
|
ProjectPermissionSub.Folders | (ForcedSubject<ProjectPermissionSub.Folders> & GenericFields)
|
||||||
| [GeneralPermissionActions, ProjectPermission.Webhooks]
|
]
|
||||||
| [GeneralPermissionActions, ProjectPermission.AuditLogs]
|
| [
|
||||||
| [GeneralPermissionActions, ProjectPermission.Environments]
|
ProjectPermissionActions,
|
||||||
| [GeneralPermissionActions, ProjectPermission.IpAllowList]
|
(
|
||||||
| [GeneralPermissionActions, ProjectPermission.Settings]
|
| ProjectPermissionSub.SecretImports
|
||||||
| [GeneralPermissionActions, ProjectPermission.ServiceTokens]
|
| (ForcedSubject<ProjectPermissionSub.SecretImports> & GenericFields)
|
||||||
| [GeneralPermissionActions.Delete, ProjectPermission.Workspace]
|
)
|
||||||
| [GeneralPermissionActions.Edit, ProjectPermission.Workspace];
|
]
|
||||||
|
| [ProjectPermissionActions, ProjectPermissionSub.Role]
|
||||||
|
| [ProjectPermissionActions, ProjectPermissionSub.Tags]
|
||||||
|
| [ProjectPermissionActions, ProjectPermissionSub.Member]
|
||||||
|
| [ProjectPermissionActions, ProjectPermissionSub.Integrations]
|
||||||
|
| [ProjectPermissionActions, ProjectPermissionSub.Webhooks]
|
||||||
|
| [ProjectPermissionActions, ProjectPermissionSub.AuditLogs]
|
||||||
|
| [ProjectPermissionActions, ProjectPermissionSub.Environments]
|
||||||
|
| [ProjectPermissionActions, ProjectPermissionSub.IpAllowList]
|
||||||
|
| [ProjectPermissionActions, ProjectPermissionSub.Settings]
|
||||||
|
| [ProjectPermissionActions, ProjectPermissionSub.ServiceTokens]
|
||||||
|
| [ProjectPermissionActions.Delete, ProjectPermissionSub.Workspace]
|
||||||
|
| [ProjectPermissionActions.Edit, ProjectPermissionSub.Workspace]
|
||||||
|
| [ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback]
|
||||||
|
| [ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback];
|
||||||
|
|
||||||
const buildAdminPermission = () => {
|
const buildAdminPermission = () => {
|
||||||
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Secrets);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.Secrets);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.Secrets);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.Secrets);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Folders);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Folders);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.Folders);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Folders);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.Folders);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Folders);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.Folders);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Folders);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.SecretImports);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretImports);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.SecretImports);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretImports);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.SecretImports);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretImports);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.SecretImports);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.SecretImports);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Member);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.Member);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.Member);
|
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.Member);
|
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Role);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.Role);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Member);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.Role);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.Role);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Member);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Integrations);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.Integrations);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Role);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.Integrations);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Role);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.Integrations);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Role);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Webhooks);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.Webhooks);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.Webhooks);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.Webhooks);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.ServiceTokens);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.ServiceTokens);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Webhooks);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.ServiceTokens);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Webhooks);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.ServiceTokens);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Webhooks);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Settings);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.Settings);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.Settings);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.ServiceTokens);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.Settings);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.ServiceTokens);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Environments);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Settings);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.Environments);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Settings);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.Environments);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.Environments);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Settings);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Tags);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.Tags);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Environments);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.Tags);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Environments);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.Tags);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Environments);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.AuditLogs);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.AuditLogs);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Tags);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.AuditLogs);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Tags);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.AuditLogs);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Tags);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.IpAllowList);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.IpAllowList);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.AuditLogs);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.IpAllowList);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.AuditLogs);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.IpAllowList);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.AuditLogs);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.Workspace);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.IpAllowList);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
|
||||||
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.IpAllowList);
|
||||||
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.IpAllowList);
|
||||||
|
|
||||||
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Workspace);
|
||||||
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.IpAllowList);
|
||||||
|
|
||||||
return build();
|
return build();
|
||||||
};
|
};
|
||||||
@@ -123,31 +152,33 @@ export const adminProjectPermissions = buildAdminPermission();
|
|||||||
const buildMemberPermission = () => {
|
const buildMemberPermission = () => {
|
||||||
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Secrets);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.Secrets);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.Secrets);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.Secrets);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Folders);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Folders);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.Folders);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.Folders);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.Folders);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.Folders);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.Folders);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.Folders);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.SecretImports);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretImports);
|
||||||
can(GeneralPermissionActions.Create, ProjectPermission.SecretImports);
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretImports);
|
||||||
can(GeneralPermissionActions.Edit, ProjectPermission.SecretImports);
|
can(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretImports);
|
||||||
can(GeneralPermissionActions.Delete, ProjectPermission.SecretImports);
|
can(ProjectPermissionActions.Delete, ProjectPermissionSub.SecretImports);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||||
|
can(ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Member);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Role);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Integrations);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Webhooks);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.ServiceTokens);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Settings);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Settings);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Environments);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Tags);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.AuditLogs);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.IpAllowList);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
||||||
|
|
||||||
return build();
|
return build();
|
||||||
};
|
};
|
||||||
@@ -157,19 +188,20 @@ export const memberProjectPermissions = buildMemberPermission();
|
|||||||
const buildViewerPermission = () => {
|
const buildViewerPermission = () => {
|
||||||
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
const { can, build } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||||
|
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Secrets);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Folders);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Folders);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.SecretImports);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretImports);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Member);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Role);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Integrations);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Webhooks);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.ServiceTokens);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Settings);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Environments);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Settings);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.Tags);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.AuditLogs);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
|
||||||
can(GeneralPermissionActions.Read, ProjectPermission.IpAllowList);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
||||||
|
|
||||||
return build();
|
return build();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const GetActionV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
actionId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const AddUserActionV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
action: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetUserActionV1 = z.object({
|
||||||
|
query: z.object({
|
||||||
|
action: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const BeginEmailSignUpV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().email().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const VerifyEmailSignUpV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().email().trim(),
|
||||||
|
code: z.string().email().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Login1V1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().email().trim(),
|
||||||
|
clientPublicKey: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Login2V1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().email().trim(),
|
||||||
|
clientProof: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Srp1V1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
clientPublicKey: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChangePasswordV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
clientProof: z.string().trim(),
|
||||||
|
protectedKey: z.string().trim(),
|
||||||
|
protectedKeyIV: z.string().trim(),
|
||||||
|
protectedKeyTag: z.string().trim(),
|
||||||
|
encryptedPrivateKey: z.string().trim(),
|
||||||
|
encryptedPrivateKeyIV: z.string().trim(),
|
||||||
|
encryptedPrivateKeyTag: z.string().trim(),
|
||||||
|
salt: z.string().trim(),
|
||||||
|
verifier: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const EmailPasswordResetV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().email().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const EmailPasswordResetVerifyV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().email().trim(),
|
||||||
|
code: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CreateBackupPrivateKeyV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
clientProof: z.string().trim(),
|
||||||
|
encryptedPrivateKey: z.string().trim(),
|
||||||
|
iv: z.string().trim(),
|
||||||
|
tag: z.string().trim(),
|
||||||
|
salt: z.string().trim(),
|
||||||
|
verifier: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ResetPasswordV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
protectedKey: z.string().trim(),
|
||||||
|
protectedKeyIV: z.string().trim(),
|
||||||
|
protectedKeyTag: z.string().trim(),
|
||||||
|
encryptedPrivateKey: z.string().trim(),
|
||||||
|
encryptedPrivateKeyIV: z.string().trim(),
|
||||||
|
encryptedPrivateKeyTag: z.string().trim(),
|
||||||
|
salt: z.string().trim(),
|
||||||
|
verifier: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SendMfaTokenV2 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().email().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const VerifyMfaTokenV2 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().email().trim(),
|
||||||
|
mfaToken: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Login1V3 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().email().trim(),
|
||||||
|
providerAuthToken: z.string().trim().optional(),
|
||||||
|
clientPublicKey: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Login2V3 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().email().trim(),
|
||||||
|
providerAuthToken: z.string().trim().optional(),
|
||||||
|
clientProof: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CompletedAccountSignupV3 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().email().trim(),
|
||||||
|
firstName: z.string().trim(),
|
||||||
|
lastName: z.string().trim().optional().nullish(),
|
||||||
|
protectedKey: z.string().trim(),
|
||||||
|
protectedKeyIV: z.string().trim(),
|
||||||
|
protectedKeyTag: z.string().trim(),
|
||||||
|
publicKey: z.string().trim(),
|
||||||
|
encryptedPrivateKey: z.string().trim(),
|
||||||
|
encryptedPrivateKeyIV: z.string().trim(),
|
||||||
|
encryptedPrivateKeyTag: z.string().trim(),
|
||||||
|
salt: z.string().trim(),
|
||||||
|
verifier: z.string().trim(),
|
||||||
|
organizationName: z.string().trim(),
|
||||||
|
providerAuthToken: z.string().trim().optional().nullish(),
|
||||||
|
attributionSource: z.string().trim().optional()
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -1,15 +1,10 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import {
|
import { Bot, IUser } from "../models";
|
||||||
Bot,
|
|
||||||
IUser,
|
|
||||||
} from "../models";
|
|
||||||
import { validateUserClientForWorkspace } from "./user";
|
import { validateUserClientForWorkspace } from "./user";
|
||||||
import {
|
import { BotNotFoundError, UnauthorizedRequestError } from "../utils/errors";
|
||||||
BotNotFoundError,
|
|
||||||
UnauthorizedRequestError,
|
|
||||||
} from "../utils/errors";
|
|
||||||
import { AuthData } from "../interfaces/middleware";
|
import { AuthData } from "../interfaces/middleware";
|
||||||
import { ActorType } from "../ee/models";
|
import { ActorType } from "../ee/models";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate authenticated clients for bot with id [botId] based
|
* Validate authenticated clients for bot with id [botId] based
|
||||||
@@ -22,7 +17,7 @@ import { ActorType } from "../ee/models";
|
|||||||
export const validateClientForBot = async ({
|
export const validateClientForBot = async ({
|
||||||
authData,
|
authData,
|
||||||
botId,
|
botId,
|
||||||
acceptedRoles,
|
acceptedRoles
|
||||||
}: {
|
}: {
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
botId: Types.ObjectId;
|
botId: Types.ObjectId;
|
||||||
@@ -36,12 +31,33 @@ export const validateClientForBot = async ({
|
|||||||
await validateUserClientForWorkspace({
|
await validateUserClientForWorkspace({
|
||||||
user: authData.authPayload as IUser,
|
user: authData.authPayload as IUser,
|
||||||
workspaceId: bot.workspace,
|
workspaceId: bot.workspace,
|
||||||
acceptedRoles,
|
acceptedRoles
|
||||||
});
|
});
|
||||||
return bot;
|
return bot;
|
||||||
case ActorType.SERVICE:
|
case ActorType.SERVICE:
|
||||||
throw UnauthorizedRequestError({
|
throw UnauthorizedRequestError({
|
||||||
message: "Failed service token authorization for bot",
|
message: "Failed service token authorization for bot"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const GetBotByWorkspaceIdV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SetBotActiveStateV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
isActive: z.boolean(),
|
||||||
|
botKey: z
|
||||||
|
.object({
|
||||||
|
nonce: z.string().trim().optional(),
|
||||||
|
encryptedKey: z.string().trim().optional()
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
}),
|
||||||
|
params: z.object({
|
||||||
|
botId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const GetCloudProductsV1 = z.object({
|
||||||
|
query: z.object({
|
||||||
|
"billing-cycle": z.enum(["monthly", "yearly"])
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const CreateWorkspaceEnvironmentV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
environmentSlug: z.string().trim(),
|
||||||
|
environmentName: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateWorkspaceEnvironmentV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
environmentSlug: z.string().trim(),
|
||||||
|
environmentName: z.string().trim(),
|
||||||
|
oldEnvironmentSlug: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteWorkspaceEnvironmentV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
environmentSlug: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetAllAccessibileEnvironmentsOfWorkspaceV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const CreateFolderV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
folderName: z.string().trim(),
|
||||||
|
parentFolderId: z.string().trim().optional()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateFolderV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
folderId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
name: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteFolderV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
folderId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetFoldersV1 = z.object({
|
||||||
|
query: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
parentFolderId: z.string().trim().optional(),
|
||||||
|
parentFolderPath: z.string().trim().optional()
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -1,18 +1,15 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import {
|
import { IUser, Integration, IntegrationAuth } from "../models";
|
||||||
IUser,
|
|
||||||
Integration,
|
|
||||||
IntegrationAuth,
|
|
||||||
} from "../models";
|
|
||||||
import { validateUserClientForWorkspace } from "./user";
|
import { validateUserClientForWorkspace } from "./user";
|
||||||
import { IntegrationService } from "../services";
|
import { IntegrationService } from "../services";
|
||||||
import {
|
import {
|
||||||
IntegrationAuthNotFoundError,
|
IntegrationAuthNotFoundError,
|
||||||
IntegrationNotFoundError,
|
IntegrationNotFoundError,
|
||||||
UnauthorizedRequestError,
|
UnauthorizedRequestError
|
||||||
} from "../utils/errors";
|
} from "../utils/errors";
|
||||||
import { AuthData } from "../interfaces/middleware";
|
import { AuthData } from "../interfaces/middleware";
|
||||||
import { ActorType } from "../ee/models";
|
import { ActorType } from "../ee/models";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate authenticated clients for integration with id [integrationId] based
|
* Validate authenticated clients for integration with id [integrationId] based
|
||||||
@@ -25,42 +22,80 @@ import { ActorType } from "../ee/models";
|
|||||||
* @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint
|
* @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint
|
||||||
*/
|
*/
|
||||||
export const validateClientForIntegration = async ({
|
export const validateClientForIntegration = async ({
|
||||||
authData,
|
authData,
|
||||||
integrationId,
|
integrationId,
|
||||||
acceptedRoles,
|
acceptedRoles
|
||||||
}: {
|
}: {
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
integrationId: Types.ObjectId;
|
integrationId: Types.ObjectId;
|
||||||
acceptedRoles: Array<"admin" | "member">;
|
acceptedRoles: Array<"admin" | "member">;
|
||||||
}) => {
|
}) => {
|
||||||
|
const integration = await Integration.findById(integrationId);
|
||||||
|
if (!integration) throw IntegrationNotFoundError();
|
||||||
|
|
||||||
const integration = await Integration.findById(integrationId);
|
const integrationAuth = await IntegrationAuth.findById(integration.integrationAuth).select(
|
||||||
if (!integration) throw IntegrationNotFoundError();
|
"+refreshCiphertext +refreshIV +refreshTag +accessCiphertext +accessIV +accessTag +accessExpiresAt"
|
||||||
|
);
|
||||||
|
|
||||||
const integrationAuth = await IntegrationAuth
|
if (!integrationAuth) throw IntegrationAuthNotFoundError();
|
||||||
.findById(integration.integrationAuth)
|
|
||||||
.select(
|
|
||||||
"+refreshCiphertext +refreshIV +refreshTag +accessCiphertext +accessIV +accessTag +accessExpiresAt"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!integrationAuth) throw IntegrationAuthNotFoundError();
|
const accessToken = (
|
||||||
|
await IntegrationService.getIntegrationAuthAccess({
|
||||||
|
integrationAuthId: integrationAuth._id
|
||||||
|
})
|
||||||
|
).accessToken;
|
||||||
|
|
||||||
const accessToken = (await IntegrationService.getIntegrationAuthAccess({
|
switch (authData.actor.type) {
|
||||||
integrationAuthId: integrationAuth._id,
|
case ActorType.USER:
|
||||||
})).accessToken;
|
await validateUserClientForWorkspace({
|
||||||
|
user: authData.authPayload as IUser,
|
||||||
|
workspaceId: integration.workspace,
|
||||||
|
acceptedRoles
|
||||||
|
});
|
||||||
|
|
||||||
switch (authData.actor.type) {
|
return { integration, accessToken };
|
||||||
case ActorType.USER:
|
case ActorType.SERVICE:
|
||||||
await validateUserClientForWorkspace({
|
throw UnauthorizedRequestError({
|
||||||
user: authData.authPayload as IUser,
|
message: "Failed service token authorization for integration"
|
||||||
workspaceId: integration.workspace,
|
});
|
||||||
acceptedRoles,
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
return ({ integration, accessToken });
|
export const CreateIntegrationV1 = z.object({
|
||||||
case ActorType.SERVICE:
|
body: z.object({
|
||||||
throw UnauthorizedRequestError({
|
integrationAuthId: z.string().trim(),
|
||||||
message: "Failed service token authorization for integration",
|
app: z.string().trim(),
|
||||||
});
|
isActive: z.boolean(),
|
||||||
}
|
appId: z.string().trim(),
|
||||||
}
|
secretPath: z.string().trim().default("/"),
|
||||||
|
sourceEnvironment: z.string().trim(),
|
||||||
|
targetEnvironment: z.string().trim(),
|
||||||
|
targetEnvironmentId: z.string().trim(),
|
||||||
|
targetService: z.string().trim(),
|
||||||
|
targetServiceId: z.string().trim(),
|
||||||
|
owner: z.string().trim(),
|
||||||
|
path: z.string().trim(),
|
||||||
|
region: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateIntegrationV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
integrationId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
app: z.string().trim(),
|
||||||
|
appId: z.string().trim(),
|
||||||
|
isActive: z.boolean(),
|
||||||
|
secretPath: z.string().trim().default("/"),
|
||||||
|
targetEnvironment: z.string().trim(),
|
||||||
|
owner: z.string().trim(),
|
||||||
|
environment: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteIntegrationV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
integrationId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import {
|
import { IUser, IWorkspace, IntegrationAuth } from "../models";
|
||||||
IUser,
|
import { IntegrationAuthNotFoundError, UnauthorizedRequestError } from "../utils/errors";
|
||||||
IWorkspace,
|
|
||||||
IntegrationAuth,
|
|
||||||
} from "../models";
|
|
||||||
import {
|
|
||||||
IntegrationAuthNotFoundError,
|
|
||||||
UnauthorizedRequestError,
|
|
||||||
} from "../utils/errors";
|
|
||||||
import { IntegrationService } from "../services";
|
import { IntegrationService } from "../services";
|
||||||
import { validateUserClientForWorkspace } from "./user";
|
import { validateUserClientForWorkspace } from "./user";
|
||||||
import { AuthData } from "../interfaces/middleware";
|
import { AuthData } from "../interfaces/middleware";
|
||||||
import { ActorType } from "../ee/models";
|
import { ActorType } from "../ee/models";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate authenticated clients for integration authorization with id [integrationAuthId] based
|
* Validate authenticated clients for integration authorization with id [integrationAuthId] based
|
||||||
@@ -22,53 +16,147 @@ import { ActorType } from "../ee/models";
|
|||||||
* @param {Array<'admin' | 'member'>} obj.acceptedRoles - accepted workspace roles
|
* @param {Array<'admin' | 'member'>} obj.acceptedRoles - accepted workspace roles
|
||||||
* @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint
|
* @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint
|
||||||
*/
|
*/
|
||||||
const validateClientForIntegrationAuth = async ({
|
const validateClientForIntegrationAuth = async ({
|
||||||
authData,
|
authData,
|
||||||
integrationAuthId,
|
integrationAuthId,
|
||||||
acceptedRoles,
|
acceptedRoles,
|
||||||
attachAccessToken,
|
attachAccessToken
|
||||||
}: {
|
}: {
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
integrationAuthId: Types.ObjectId;
|
integrationAuthId: Types.ObjectId;
|
||||||
acceptedRoles: Array<"admin" | "member">;
|
acceptedRoles: Array<"admin" | "member">;
|
||||||
attachAccessToken?: boolean;
|
attachAccessToken?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
const integrationAuth = await IntegrationAuth.findById(integrationAuthId)
|
||||||
|
.populate<{ workspace: IWorkspace }>("workspace")
|
||||||
|
.select(
|
||||||
|
"+refreshCiphertext +refreshIV +refreshTag +accessCiphertext +accessIV +accessTag +accessExpiresAt"
|
||||||
|
);
|
||||||
|
|
||||||
const integrationAuth = await IntegrationAuth
|
if (!integrationAuth) throw IntegrationAuthNotFoundError();
|
||||||
.findById(integrationAuthId)
|
|
||||||
.populate<{ workspace: IWorkspace }>("workspace")
|
|
||||||
.select(
|
|
||||||
"+refreshCiphertext +refreshIV +refreshTag +accessCiphertext +accessIV +accessTag +accessExpiresAt"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!integrationAuth) throw IntegrationAuthNotFoundError();
|
let accessToken, accessId;
|
||||||
|
if (attachAccessToken) {
|
||||||
|
const access = await IntegrationService.getIntegrationAuthAccess({
|
||||||
|
integrationAuthId: integrationAuth._id
|
||||||
|
});
|
||||||
|
|
||||||
let accessToken, accessId;
|
accessToken = access.accessToken;
|
||||||
if (attachAccessToken) {
|
accessId = access.accessId;
|
||||||
const access = (await IntegrationService.getIntegrationAuthAccess({
|
}
|
||||||
integrationAuthId: integrationAuth._id,
|
|
||||||
}));
|
|
||||||
|
|
||||||
accessToken = access.accessToken;
|
switch (authData.actor.type) {
|
||||||
accessId = access.accessId;
|
case ActorType.USER:
|
||||||
}
|
await validateUserClientForWorkspace({
|
||||||
|
user: authData.authPayload as IUser,
|
||||||
|
workspaceId: integrationAuth.workspace._id,
|
||||||
|
acceptedRoles
|
||||||
|
});
|
||||||
|
|
||||||
switch (authData.actor.type) {
|
return { integrationAuth, accessToken, accessId };
|
||||||
case ActorType.USER:
|
case ActorType.SERVICE:
|
||||||
await validateUserClientForWorkspace({
|
throw UnauthorizedRequestError({
|
||||||
user: authData.authPayload as IUser,
|
message: "Failed service token authorization for integration authorization"
|
||||||
workspaceId: integrationAuth.workspace._id,
|
});
|
||||||
acceptedRoles,
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return ({ integrationAuth, accessToken, accessId });
|
|
||||||
case ActorType.SERVICE:
|
|
||||||
throw UnauthorizedRequestError({
|
|
||||||
message: "Failed service token authorization for integration authorization",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
validateClientForIntegrationAuth,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const GetIntegrationAuthV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
integrationAuthId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const OauthExchangeV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
code: z.string().trim(),
|
||||||
|
integration: z.string().trim(),
|
||||||
|
url: z.string().trim().url().optional(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SaveIntegrationAccessTokenV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
accessId: z.string().trim().optional(),
|
||||||
|
accessToken: z.string().trim().optional(),
|
||||||
|
url: z.string().url().trim(),
|
||||||
|
namespace: z.string().trim(),
|
||||||
|
integration: z.string().trim(),
|
||||||
|
refreshToken:z.string().trim().optional()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetIntegrationAuthAppsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
integrationAuthId: z.string().trim()
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
teamId: z.string().trim(),
|
||||||
|
workspaceSlug: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetIntegrationAuthTeamsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
integrationAuthId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetIntegrationAuthVercelBranchesV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
integrationAuthId: z.string().trim()
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
appId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetIntegrationAuthRailwayEnvironmentsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
integrationAuthId: z.string().trim()
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
appId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetIntegrationAuthRailwayServicesV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
integrationAuthId: z.string().trim()
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
appId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetIntegrationAuthBitbucketWorkspacesV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
integrationAuthId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetIntegrationAuthNorthflankSecretGroupsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
integrationAuthId: z.string().trim()
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
appId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteIntegrationAuthV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
integrationAuthId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetIntegrationAuthTeamCityBuildConfigsV1 = z.object({
|
||||||
|
params:z.object({
|
||||||
|
appId:z.string().trim(),
|
||||||
|
integrationAuthId:z.string().trim()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export { validateClientForIntegrationAuth };
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const UploadKeyV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
key: z.object({
|
||||||
|
encryptedKey: z.string().trim(),
|
||||||
|
nonce: z.string().trim(),
|
||||||
|
userId: z.string().trim()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetLatestKeyV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -1,16 +1,12 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import {
|
import { IServiceTokenData, IUser, Membership } from "../models";
|
||||||
IServiceTokenData,
|
|
||||||
IUser,
|
|
||||||
Membership,
|
|
||||||
} from "../models";
|
|
||||||
import { validateUserClientForWorkspace } from "./user";
|
import { validateUserClientForWorkspace } from "./user";
|
||||||
import { validateServiceTokenDataClientForWorkspace } from "./serviceTokenData";
|
import { validateServiceTokenDataClientForWorkspace } from "./serviceTokenData";
|
||||||
import {
|
import { MembershipNotFoundError } from "../utils/errors";
|
||||||
MembershipNotFoundError,
|
|
||||||
} from "../utils/errors";
|
|
||||||
import { AuthData } from "../interfaces/middleware";
|
import { AuthData } from "../interfaces/middleware";
|
||||||
import { ActorType } from "../ee/models";
|
import { ActorType } from "../ee/models";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { ADMIN, CUSTOM, MEMBER, VIEWER } from "../variables";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate authenticated clients for membership with id [membershipId] based
|
* Validate authenticated clients for membership with id [membershipId] based
|
||||||
@@ -22,36 +18,64 @@ import { ActorType } from "../ee/models";
|
|||||||
* @returns {Membership} - validated membership
|
* @returns {Membership} - validated membership
|
||||||
*/
|
*/
|
||||||
export const validateClientForMembership = async ({
|
export const validateClientForMembership = async ({
|
||||||
authData,
|
authData,
|
||||||
membershipId,
|
membershipId,
|
||||||
acceptedRoles,
|
acceptedRoles
|
||||||
}: {
|
}: {
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
membershipId: Types.ObjectId;
|
membershipId: Types.ObjectId;
|
||||||
acceptedRoles: Array<"admin" | "member">;
|
acceptedRoles: Array<"admin" | "member">;
|
||||||
}) => {
|
}) => {
|
||||||
|
const membership = await Membership.findById(membershipId);
|
||||||
|
|
||||||
const membership = await Membership.findById(membershipId);
|
if (!membership)
|
||||||
|
throw MembershipNotFoundError({
|
||||||
|
message: "Failed to find membership"
|
||||||
|
});
|
||||||
|
|
||||||
if (!membership) throw MembershipNotFoundError({
|
switch (authData.actor.type) {
|
||||||
message: "Failed to find membership",
|
case ActorType.USER:
|
||||||
});
|
await validateUserClientForWorkspace({
|
||||||
|
user: authData.authPayload as IUser,
|
||||||
|
workspaceId: membership.workspace,
|
||||||
|
acceptedRoles
|
||||||
|
});
|
||||||
|
|
||||||
switch (authData.actor.type) {
|
return membership;
|
||||||
case ActorType.USER:
|
case ActorType.SERVICE:
|
||||||
await validateUserClientForWorkspace({
|
await validateServiceTokenDataClientForWorkspace({
|
||||||
user: authData.authPayload as IUser,
|
serviceTokenData: authData.authPayload as IServiceTokenData,
|
||||||
workspaceId: membership.workspace,
|
workspaceId: new Types.ObjectId(membership.workspace)
|
||||||
acceptedRoles,
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return membership;
|
return membership;
|
||||||
case ActorType.SERVICE:
|
}
|
||||||
await validateServiceTokenDataClientForWorkspace({
|
};
|
||||||
serviceTokenData: authData.authPayload as IServiceTokenData,
|
|
||||||
workspaceId: new Types.ObjectId(membership.workspace),
|
|
||||||
});
|
|
||||||
|
|
||||||
return membership;
|
export const ValidateMembershipV1 = z.object({
|
||||||
}
|
params: z.object({
|
||||||
}
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteMembershipV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
membershipId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChangeMembershipRoleV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
role: z.enum([ADMIN, VIEWER, MEMBER, CUSTOM])
|
||||||
|
}),
|
||||||
|
params: z.object({ membershipId: z.string().trim() })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DenyMembershipPermissionV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
membershipId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
permissions: z.object({}).array()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|||||||
@@ -197,3 +197,11 @@ export const DeleteOrgMemberv2 = z.object({
|
|||||||
export const GetOrgWorkspacesv2 = z.object({
|
export const GetOrgWorkspacesv2 = z.object({
|
||||||
params: z.object({ organizationId: z.string().trim() })
|
params: z.object({ organizationId: z.string().trim() })
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const VerfiyUserToOrganizationV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().trim().email(),
|
||||||
|
organizationId: z.string().trim(),
|
||||||
|
code: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const CreateSecretImportV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
folderId: z.string().trim().default("root"),
|
||||||
|
secretImport: z.object({
|
||||||
|
environment: z.string().trim(),
|
||||||
|
secretPath: z.string().trim()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateSecretImportV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
id: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
secretImports: z
|
||||||
|
.object({
|
||||||
|
environment: z.string().trim(),
|
||||||
|
secretPath: z.string().trim()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteSecretImportV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
id: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
secretImportPath: z.string().trim(),
|
||||||
|
secretImportEnv: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetSecretImportsV1 = z.object({
|
||||||
|
query: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
folderId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetAllSecretsFromImportV1 = z.object({
|
||||||
|
query: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
folderId: z.string().trim().default("root")
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const GetSecretSnapshotV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
secretSnapshotId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -1,18 +1,15 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import {
|
import { ISecret, IServiceTokenData, IUser, Secret } from "../models";
|
||||||
ISecret,
|
|
||||||
IServiceTokenData,
|
|
||||||
IUser,
|
|
||||||
Secret,
|
|
||||||
} from "../models";
|
|
||||||
import { validateUserClientForSecret, validateUserClientForSecrets } from "./user";
|
import { validateUserClientForSecret, validateUserClientForSecrets } from "./user";
|
||||||
import { validateServiceTokenDataClientForSecrets, validateServiceTokenDataClientForWorkspace } from "./serviceTokenData";
|
|
||||||
import {
|
import {
|
||||||
BadRequestError,
|
validateServiceTokenDataClientForSecrets,
|
||||||
SecretNotFoundError,
|
validateServiceTokenDataClientForWorkspace
|
||||||
} from "../utils/errors";
|
} from "./serviceTokenData";
|
||||||
|
import { BadRequestError, SecretNotFoundError } from "../utils/errors";
|
||||||
import { AuthData } from "../interfaces/middleware";
|
import { AuthData } from "../interfaces/middleware";
|
||||||
import { ActorType } from "../ee/models";
|
import { ActorType } from "../ee/models";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { SECRET_PERSONAL, SECRET_SHARED } from "../variables";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate authenticated clients for secrets with id [secretId] based
|
* Validate authenticated clients for secrets with id [secretId] based
|
||||||
@@ -24,42 +21,43 @@ import { ActorType } from "../ee/models";
|
|||||||
* @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint
|
* @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint
|
||||||
*/
|
*/
|
||||||
export const validateClientForSecret = async ({
|
export const validateClientForSecret = async ({
|
||||||
authData,
|
authData,
|
||||||
secretId,
|
secretId,
|
||||||
acceptedRoles,
|
acceptedRoles,
|
||||||
requiredPermissions,
|
requiredPermissions
|
||||||
}: {
|
}: {
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
secretId: Types.ObjectId;
|
secretId: Types.ObjectId;
|
||||||
acceptedRoles: Array<"admin" | "member">;
|
acceptedRoles: Array<"admin" | "member">;
|
||||||
requiredPermissions: string[];
|
requiredPermissions: string[];
|
||||||
}) => {
|
}) => {
|
||||||
const secret = await Secret.findById(secretId);
|
const secret = await Secret.findById(secretId);
|
||||||
|
|
||||||
if (!secret) throw SecretNotFoundError({
|
if (!secret)
|
||||||
message: "Failed to find secret",
|
throw SecretNotFoundError({
|
||||||
|
message: "Failed to find secret"
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (authData.actor.type) {
|
switch (authData.actor.type) {
|
||||||
case ActorType.USER:
|
case ActorType.USER:
|
||||||
await validateUserClientForSecret({
|
await validateUserClientForSecret({
|
||||||
user: authData.authPayload as IUser,
|
user: authData.authPayload as IUser,
|
||||||
secret,
|
secret,
|
||||||
acceptedRoles,
|
acceptedRoles,
|
||||||
requiredPermissions,
|
requiredPermissions
|
||||||
});
|
});
|
||||||
|
|
||||||
return secret;
|
return secret;
|
||||||
case ActorType.SERVICE:
|
case ActorType.SERVICE:
|
||||||
await validateServiceTokenDataClientForWorkspace({
|
await validateServiceTokenDataClientForWorkspace({
|
||||||
serviceTokenData: authData.authPayload as IServiceTokenData,
|
serviceTokenData: authData.authPayload as IServiceTokenData,
|
||||||
workspaceId: secret.workspace,
|
workspaceId: secret.workspace,
|
||||||
environment: secret.environment,
|
environment: secret.environment
|
||||||
});
|
});
|
||||||
|
|
||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate authenticated clients for secrets with ids [secretIds] based
|
* Validate authenticated clients for secrets with ids [secretIds] based
|
||||||
@@ -72,43 +70,292 @@ export const validateClientForSecret = async ({
|
|||||||
* @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint
|
* @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint
|
||||||
*/
|
*/
|
||||||
export const validateClientForSecrets = async ({
|
export const validateClientForSecrets = async ({
|
||||||
authData,
|
authData,
|
||||||
secretIds,
|
secretIds,
|
||||||
requiredPermissions,
|
requiredPermissions
|
||||||
}: {
|
}: {
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
secretIds: Types.ObjectId[];
|
secretIds: Types.ObjectId[];
|
||||||
requiredPermissions: string[];
|
requiredPermissions: string[];
|
||||||
}) => {
|
}) => {
|
||||||
|
let secrets: ISecret[] = [];
|
||||||
|
|
||||||
let secrets: ISecret[] = [];
|
secrets = await Secret.find({
|
||||||
|
_id: {
|
||||||
secrets = await Secret.find({
|
$in: secretIds
|
||||||
_id: {
|
|
||||||
$in: secretIds,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (secrets.length != secretIds.length) {
|
|
||||||
throw BadRequestError({ message: "Failed to validate non-existent secrets" })
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
switch (authData.actor.type) {
|
if (secrets.length != secretIds.length) {
|
||||||
case ActorType.USER:
|
throw BadRequestError({ message: "Failed to validate non-existent secrets" });
|
||||||
await validateUserClientForSecrets({
|
}
|
||||||
user: authData.authPayload as IUser,
|
|
||||||
secrets,
|
|
||||||
requiredPermissions,
|
|
||||||
});
|
|
||||||
|
|
||||||
return secrets;
|
switch (authData.actor.type) {
|
||||||
case ActorType.SERVICE:
|
case ActorType.USER:
|
||||||
await validateServiceTokenDataClientForSecrets({
|
await validateUserClientForSecrets({
|
||||||
serviceTokenData: authData.authPayload as IServiceTokenData,
|
user: authData.authPayload as IUser,
|
||||||
secrets,
|
secrets,
|
||||||
requiredPermissions,
|
requiredPermissions
|
||||||
});
|
});
|
||||||
|
|
||||||
return secrets;
|
return secrets;
|
||||||
}
|
case ActorType.SERVICE:
|
||||||
}
|
await validateServiceTokenDataClientForSecrets({
|
||||||
|
serviceTokenData: authData.authPayload as IServiceTokenData,
|
||||||
|
secrets,
|
||||||
|
requiredPermissions
|
||||||
|
});
|
||||||
|
|
||||||
|
return secrets;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GetSecretVersionsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
secretId: z.string().trim()
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
offset: z.number(),
|
||||||
|
limit: z.number()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const RollbackSecretVersionV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
secretId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
version: z.number()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PushSecretsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
secrets: z.object({}).array(),
|
||||||
|
keys: z.object({}).array(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
channel: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PullSecretsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
channel: z.string().optional(),
|
||||||
|
environment: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PullSecretsServiceTokenV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
channel: z.string().optional(),
|
||||||
|
environment: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const batchUpdateRequestV2 = z.object({
|
||||||
|
_id: z.string(),
|
||||||
|
folderId: z.string().trim().optional(),
|
||||||
|
type: z.enum(["shared", "personal"]),
|
||||||
|
secretName: z.string().trim(),
|
||||||
|
secretKeyCiphertext: z.string().trim(),
|
||||||
|
secretKeyIV: z.string().trim(),
|
||||||
|
secretKeyTag: z.string().trim(),
|
||||||
|
secretValueCiphertext: z.string().trim(),
|
||||||
|
secretValueIV: z.string().trim(),
|
||||||
|
secretValueTag: z.string().trim(),
|
||||||
|
secretCommentCiphertext: z.string().trim().optional(),
|
||||||
|
secretCommentIV: z.string().trim().optional(),
|
||||||
|
secretCommentTag: z.string().trim().optional(),
|
||||||
|
tags: z.object({
|
||||||
|
_id: z.string().trim(),
|
||||||
|
name: z.string().trim(),
|
||||||
|
slug: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const BatchSecretsV2 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
folderId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
secretPath: z.string().trim().optional(),
|
||||||
|
requests: z
|
||||||
|
.discriminatedUnion("method", [
|
||||||
|
z.object({
|
||||||
|
method: z.literal("POST"),
|
||||||
|
secret: batchUpdateRequestV2.omit({ _id: true })
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
method: z.literal("PATCH"),
|
||||||
|
secret: batchUpdateRequestV2
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
method: z.literal("DELETE"),
|
||||||
|
secret: z.object({ _id: z.string().trim(), secretName: z.string().trim() })
|
||||||
|
})
|
||||||
|
])
|
||||||
|
.array()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetSecretsV2 = z.object({
|
||||||
|
query: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
tagSlugs: z.string().trim().optional(),
|
||||||
|
folderId: z.string().trim().default("root"),
|
||||||
|
secretPath: z.string().trim().optional(),
|
||||||
|
include_imports: z
|
||||||
|
.enum(["true", "false"])
|
||||||
|
.default("false")
|
||||||
|
.transform((value) => value === "true")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetSecretsRawV3 = z.object({
|
||||||
|
query: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
secretPath: z.string().trim().default("/"),
|
||||||
|
folderId:z.string().trim().optional(),
|
||||||
|
include_imports: z
|
||||||
|
.enum(["true", "false"])
|
||||||
|
.default("false")
|
||||||
|
.transform((value) => value === "true")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetSecretByNameRawV3 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
secretName: z.string().trim()
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
secretPath: z.string().trim().default("/"),
|
||||||
|
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]).optional()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CreateSecretRawV3 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
secretPath: z.string().trim().default("/"),
|
||||||
|
secretValue: z.string().trim(),
|
||||||
|
secretComment: z.string().trim(),
|
||||||
|
type: z.enum([SECRET_SHARED, SECRET_PERSONAL])
|
||||||
|
}),
|
||||||
|
params: z.object({
|
||||||
|
secretName: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateSecretByNameRawV3 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
secretName: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
secretValue: z.string().trim(),
|
||||||
|
secretName: z.string().trim(),
|
||||||
|
secretPath: z.string().trim().default("/"),
|
||||||
|
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]).default(SECRET_SHARED)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteSecretByNameRawV3 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
secretName: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
secretPath: z.string().trim().default("/"),
|
||||||
|
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]).default(SECRET_SHARED)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetSecretsV3 = z.object({
|
||||||
|
query: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
secretPath: z.string().trim().default("/"),
|
||||||
|
folderId: z.string().trim().optional(),
|
||||||
|
include_imports: z
|
||||||
|
.enum(["true", "false"])
|
||||||
|
.default("false")
|
||||||
|
.transform((value) => value === "true")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetSecretByNameV3 = z.object({
|
||||||
|
query: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
secretPath: z.string().trim().default("/"),
|
||||||
|
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]).optional()
|
||||||
|
}),
|
||||||
|
params: z.object({
|
||||||
|
secretName: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CreateSecretV3 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]),
|
||||||
|
secretPath: z.string().trim().default("/"),
|
||||||
|
secretName: z.string().trim(),
|
||||||
|
secretKeyCiphertext: z.string().trim(),
|
||||||
|
secretKeyIV: z.string().trim(),
|
||||||
|
secretKeyTag: z.string().trim(),
|
||||||
|
secretValueCiphertext: z.string().trim(),
|
||||||
|
secretValueIV: z.string().trim(),
|
||||||
|
secretValueTag: z.string().trim(),
|
||||||
|
secretCommentCiphertext: z.string().trim().optional(),
|
||||||
|
secretCommentIV: z.string().trim().optional(),
|
||||||
|
secretCommentTag: z.string().trim().optional(),
|
||||||
|
metadata: z.object({
|
||||||
|
source: z.string()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateSecretByNameV3 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]),
|
||||||
|
secretPath: z.string().trim().default("/"),
|
||||||
|
secretValueCiphertext: z.string().trim(),
|
||||||
|
secretValueIV: z.string().trim(),
|
||||||
|
secretValueTag: z.string().trim()
|
||||||
|
}),
|
||||||
|
params: z.object({
|
||||||
|
secretName: z.string()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteSecretByNameV3 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
type: z.enum([SECRET_SHARED, SECRET_PERSONAL]),
|
||||||
|
secretPath: z.string().trim().default("/")
|
||||||
|
}),
|
||||||
|
params: z.object({
|
||||||
|
secretName: z.string()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import {
|
import { ISecret, IServiceTokenData, IUser, ServiceTokenData } from "../models";
|
||||||
ISecret,
|
|
||||||
IServiceTokenData,
|
|
||||||
IUser,
|
|
||||||
ServiceTokenData,
|
|
||||||
} from "../models";
|
|
||||||
import { ServiceTokenDataNotFoundError, UnauthorizedRequestError } from "../utils/errors";
|
import { ServiceTokenDataNotFoundError, UnauthorizedRequestError } from "../utils/errors";
|
||||||
import { validateUserClientForWorkspace } from "./user";
|
import { validateUserClientForWorkspace } from "./user";
|
||||||
import { ActorType } from "../ee/models";
|
import { ActorType } from "../ee/models";
|
||||||
import { AuthData } from "../interfaces/middleware";
|
import { AuthData } from "../interfaces/middleware";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate authenticated clients for service token with id [serviceTokenId] based
|
* Validate authenticated clients for service token with id [serviceTokenId] based
|
||||||
@@ -31,9 +27,10 @@ export const validateClientForServiceTokenData = async ({
|
|||||||
.select("+encryptedKey +iv +tag")
|
.select("+encryptedKey +iv +tag")
|
||||||
.populate<{ user: IUser }>("user");
|
.populate<{ user: IUser }>("user");
|
||||||
|
|
||||||
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({
|
if (!serviceTokenData)
|
||||||
message: "Failed to find service token data"
|
throw ServiceTokenDataNotFoundError({
|
||||||
});
|
message: "Failed to find service token data"
|
||||||
|
});
|
||||||
|
|
||||||
switch (authData.actor.type) {
|
switch (authData.actor.type) {
|
||||||
case ActorType.USER:
|
case ActorType.USER:
|
||||||
@@ -140,3 +137,28 @@ export const validateServiceTokenDataClientForSecrets = async ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const CreateServiceTokenV2 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
name: z.string().trim(),
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
scopes: z
|
||||||
|
.object({
|
||||||
|
environment: z.string().trim(),
|
||||||
|
secretPath: z.string().trim()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.min(1),
|
||||||
|
encryptedKey: z.string().trim(),
|
||||||
|
iv: z.string().trim(),
|
||||||
|
tag: z.string().trim(),
|
||||||
|
expiresIn: z.number(),
|
||||||
|
permissions: z.enum(["read", "write"]).array()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteServiceTokenV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
serviceTokenDataId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const GetWorkspaceTagsV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteWorkspaceTagsV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
tagId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CreateWorkspaceTagsV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
name: z.string().trim(),
|
||||||
|
slug: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
+178
-144
@@ -1,39 +1,30 @@
|
|||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import {
|
import { IOrganization, ISecret, IServiceAccount, IUser, Membership } from "../models";
|
||||||
IOrganization,
|
|
||||||
ISecret,
|
|
||||||
IServiceAccount,
|
|
||||||
IUser,
|
|
||||||
Membership,
|
|
||||||
} from "../models";
|
|
||||||
import { validateMembership } from "../helpers/membership";
|
import { validateMembership } from "../helpers/membership";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { BadRequestError, UnauthorizedRequestError, ValidationError } from "../utils/errors";
|
import { BadRequestError, UnauthorizedRequestError, ValidationError } from "../utils/errors";
|
||||||
import {
|
import { validateMembershipOrg } from "../helpers/membershipOrg";
|
||||||
validateMembershipOrg,
|
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../variables";
|
||||||
} from "../helpers/membershipOrg";
|
import { AuthMethod } from "../models";
|
||||||
import {
|
import { z } from "zod";
|
||||||
PERMISSION_READ_SECRETS,
|
|
||||||
PERMISSION_WRITE_SECRETS,
|
|
||||||
} from "../variables";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that email [email] is not disposable
|
* Validate that email [email] is not disposable
|
||||||
* @param email - email to validate
|
* @param email - email to validate
|
||||||
*/
|
*/
|
||||||
export const validateUserEmail = (email: string) => {
|
export const validateUserEmail = (email: string) => {
|
||||||
const emailDomain = email.split("@")[1];
|
const emailDomain = email.split("@")[1];
|
||||||
const disposableEmails = fs.readFileSync(
|
const disposableEmails = fs
|
||||||
path.resolve(__dirname, "../data/" + "disposable_emails.txt"),
|
.readFileSync(path.resolve(__dirname, "../data/" + "disposable_emails.txt"), "utf8")
|
||||||
"utf8"
|
.split("\n");
|
||||||
).split("\n");
|
|
||||||
|
|
||||||
if (disposableEmails.includes(emailDomain)) throw ValidationError({
|
if (disposableEmails.includes(emailDomain))
|
||||||
message: "Failed to validate email as non-disposable",
|
throw ValidationError({
|
||||||
});
|
message: "Failed to validate email as non-disposable"
|
||||||
}
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that user (client) can access workspace
|
* Validate that user (client) can access workspace
|
||||||
@@ -46,48 +37,53 @@ export const validateUserEmail = (email: string) => {
|
|||||||
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
||||||
*/
|
*/
|
||||||
export const validateUserClientForWorkspace = async ({
|
export const validateUserClientForWorkspace = async ({
|
||||||
user,
|
user,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
acceptedRoles,
|
acceptedRoles,
|
||||||
requiredPermissions,
|
requiredPermissions
|
||||||
}: {
|
}: {
|
||||||
user: IUser;
|
user: IUser;
|
||||||
workspaceId: Types.ObjectId;
|
workspaceId: Types.ObjectId;
|
||||||
environment?: string;
|
environment?: string;
|
||||||
acceptedRoles: Array<"admin" | "member">;
|
acceptedRoles: Array<"admin" | "member">;
|
||||||
requiredPermissions?: string[];
|
requiredPermissions?: string[];
|
||||||
}) => {
|
}) => {
|
||||||
|
// validate user membership in workspace
|
||||||
|
const membership = await validateMembership({
|
||||||
|
userId: user._id,
|
||||||
|
workspaceId,
|
||||||
|
acceptedRoles
|
||||||
|
});
|
||||||
|
|
||||||
// validate user membership in workspace
|
let runningIsDisallowed = false;
|
||||||
const membership = await validateMembership({
|
requiredPermissions?.forEach((requiredPermission: string) => {
|
||||||
userId: user._id,
|
switch (requiredPermission) {
|
||||||
workspaceId,
|
case PERMISSION_READ_SECRETS:
|
||||||
acceptedRoles,
|
runningIsDisallowed = _.some(membership.deniedPermissions, {
|
||||||
});
|
environmentSlug: environment,
|
||||||
|
ability: PERMISSION_READ_SECRETS
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case PERMISSION_WRITE_SECRETS:
|
||||||
|
runningIsDisallowed = _.some(membership.deniedPermissions, {
|
||||||
|
environmentSlug: environment,
|
||||||
|
ability: PERMISSION_WRITE_SECRETS
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
let runningIsDisallowed = false;
|
if (runningIsDisallowed) {
|
||||||
requiredPermissions?.forEach((requiredPermission: string) => {
|
throw UnauthorizedRequestError({
|
||||||
switch (requiredPermission) {
|
message: `Failed permissions authorization for workspace environment action : ${requiredPermission}`
|
||||||
case PERMISSION_READ_SECRETS:
|
});
|
||||||
runningIsDisallowed = _.some(membership.deniedPermissions, { environmentSlug: environment, ability: PERMISSION_READ_SECRETS });
|
}
|
||||||
break;
|
});
|
||||||
case PERMISSION_WRITE_SECRETS:
|
|
||||||
runningIsDisallowed = _.some(membership.deniedPermissions, { environmentSlug: environment, ability: PERMISSION_WRITE_SECRETS });
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (runningIsDisallowed) {
|
return membership;
|
||||||
throw UnauthorizedRequestError({
|
};
|
||||||
message: `Failed permissions authorization for workspace environment action : ${requiredPermission}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return membership;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that user (client) can access secret [secret]
|
* Validate that user (client) can access secret [secret]
|
||||||
@@ -98,32 +94,35 @@ export const validateUserClientForWorkspace = async ({
|
|||||||
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
||||||
*/
|
*/
|
||||||
export const validateUserClientForSecret = async ({
|
export const validateUserClientForSecret = async ({
|
||||||
user,
|
user,
|
||||||
secret,
|
secret,
|
||||||
acceptedRoles,
|
acceptedRoles,
|
||||||
requiredPermissions,
|
requiredPermissions
|
||||||
}: {
|
}: {
|
||||||
user: IUser;
|
user: IUser;
|
||||||
secret: ISecret;
|
secret: ISecret;
|
||||||
acceptedRoles?: Array<"admin" | "member">;
|
acceptedRoles?: Array<"admin" | "member">;
|
||||||
requiredPermissions?: string[];
|
requiredPermissions?: string[];
|
||||||
}) => {
|
}) => {
|
||||||
const membership = await validateMembership({
|
const membership = await validateMembership({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
workspaceId: secret.workspace,
|
workspaceId: secret.workspace,
|
||||||
acceptedRoles,
|
acceptedRoles
|
||||||
});
|
});
|
||||||
|
|
||||||
if (requiredPermissions?.includes(PERMISSION_WRITE_SECRETS)) {
|
if (requiredPermissions?.includes(PERMISSION_WRITE_SECRETS)) {
|
||||||
const isDisallowed = _.some(membership.deniedPermissions, { environmentSlug: secret.environment, ability: PERMISSION_WRITE_SECRETS });
|
const isDisallowed = _.some(membership.deniedPermissions, {
|
||||||
|
environmentSlug: secret.environment,
|
||||||
|
ability: PERMISSION_WRITE_SECRETS
|
||||||
|
});
|
||||||
|
|
||||||
if (isDisallowed) {
|
if (isDisallowed) {
|
||||||
throw UnauthorizedRequestError({
|
throw UnauthorizedRequestError({
|
||||||
message: "You do not have the required permissions to perform this action",
|
message: "You do not have the required permissions to perform this action"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that user (client) can access secrets [secrets]
|
* Validate that user (client) can access secrets [secrets]
|
||||||
@@ -134,41 +133,44 @@ export const validateUserClientForSecret = async ({
|
|||||||
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
||||||
*/
|
*/
|
||||||
export const validateUserClientForSecrets = async ({
|
export const validateUserClientForSecrets = async ({
|
||||||
user,
|
user,
|
||||||
secrets,
|
secrets,
|
||||||
requiredPermissions,
|
requiredPermissions
|
||||||
}: {
|
}: {
|
||||||
user: IUser;
|
user: IUser;
|
||||||
secrets: ISecret[];
|
secrets: ISecret[];
|
||||||
requiredPermissions?: string[];
|
requiredPermissions?: string[];
|
||||||
}) => {
|
}) => {
|
||||||
|
// TODO: add acceptedRoles?
|
||||||
|
|
||||||
// TODO: add acceptedRoles?
|
const userMemberships = await Membership.find({ user: user._id });
|
||||||
|
const userMembershipById = _.keyBy(userMemberships, "workspace");
|
||||||
|
const workspaceIdsSet = new Set(userMemberships.map((m) => m.workspace.toString()));
|
||||||
|
|
||||||
const userMemberships = await Membership.find({ user: user._id })
|
// for each secret check if the secret belongs to a workspace the user is a member of
|
||||||
const userMembershipById = _.keyBy(userMemberships, "workspace");
|
secrets.forEach((secret: ISecret) => {
|
||||||
const workspaceIdsSet = new Set(userMemberships.map((m) => m.workspace.toString()));
|
if (!workspaceIdsSet.has(secret.workspace.toString())) {
|
||||||
|
throw BadRequestError({
|
||||||
|
message: "Failed authorization for the secret"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// for each secret check if the secret belongs to a workspace the user is a member of
|
if (requiredPermissions?.includes(PERMISSION_WRITE_SECRETS)) {
|
||||||
secrets.forEach((secret: ISecret) => {
|
const deniedMembershipPermissions =
|
||||||
if (!workspaceIdsSet.has(secret.workspace.toString())) {
|
userMembershipById[secret.workspace.toString()].deniedPermissions;
|
||||||
throw BadRequestError({
|
const isDisallowed = _.some(deniedMembershipPermissions, {
|
||||||
message: "Failed authorization for the secret",
|
environmentSlug: secret.environment,
|
||||||
});
|
ability: PERMISSION_WRITE_SECRETS
|
||||||
}
|
});
|
||||||
|
|
||||||
if (requiredPermissions?.includes(PERMISSION_WRITE_SECRETS)) {
|
if (isDisallowed) {
|
||||||
const deniedMembershipPermissions = userMembershipById[secret.workspace.toString()].deniedPermissions;
|
throw UnauthorizedRequestError({
|
||||||
const isDisallowed = _.some(deniedMembershipPermissions, { environmentSlug: secret.environment, ability: PERMISSION_WRITE_SECRETS });
|
message: "You do not have the required permissions to perform this action"
|
||||||
|
});
|
||||||
if (isDisallowed) {
|
}
|
||||||
throw UnauthorizedRequestError({
|
}
|
||||||
message: "You do not have the required permissions to perform this action",
|
});
|
||||||
});
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that user (client) can access service account [serviceAccount]
|
* Validate that user (client) can access service account [serviceAccount]
|
||||||
@@ -179,25 +181,25 @@ export const validateUserClientForSecrets = async ({
|
|||||||
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
||||||
*/
|
*/
|
||||||
export const validateUserClientForServiceAccount = async ({
|
export const validateUserClientForServiceAccount = async ({
|
||||||
user,
|
user,
|
||||||
serviceAccount,
|
serviceAccount,
|
||||||
requiredPermissions,
|
requiredPermissions
|
||||||
}: {
|
}: {
|
||||||
user: IUser;
|
user: IUser;
|
||||||
serviceAccount: IServiceAccount;
|
serviceAccount: IServiceAccount;
|
||||||
requiredPermissions?: string[];
|
requiredPermissions?: string[];
|
||||||
}) => {
|
}) => {
|
||||||
if (!serviceAccount.user.equals(user._id)) {
|
if (!serviceAccount.user.equals(user._id)) {
|
||||||
// case: user who created service account is not the
|
// case: user who created service account is not the
|
||||||
// same user that is on the request
|
// same user that is on the request
|
||||||
await validateMembershipOrg({
|
await validateMembershipOrg({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
organizationId: serviceAccount.organization,
|
organizationId: serviceAccount.organization,
|
||||||
acceptedRoles: [],
|
acceptedRoles: [],
|
||||||
acceptedStatuses: [],
|
acceptedStatuses: []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that user (client) can access organization [organization]
|
* Validate that user (client) can access organization [organization]
|
||||||
@@ -206,22 +208,54 @@ export const validateUserClientForServiceAccount = async ({
|
|||||||
* @param {Organization} obj.organization - organization to validate against
|
* @param {Organization} obj.organization - organization to validate against
|
||||||
*/
|
*/
|
||||||
export const validateUserClientForOrganization = async ({
|
export const validateUserClientForOrganization = async ({
|
||||||
user,
|
user,
|
||||||
organization,
|
organization,
|
||||||
acceptedRoles,
|
acceptedRoles,
|
||||||
acceptedStatuses,
|
acceptedStatuses
|
||||||
}: {
|
}: {
|
||||||
user: IUser;
|
user: IUser;
|
||||||
organization: IOrganization;
|
organization: IOrganization;
|
||||||
acceptedRoles: Array<"owner" | "admin" | "member">;
|
acceptedRoles: Array<"owner" | "admin" | "member">;
|
||||||
acceptedStatuses: Array<"invited" | "accepted">;
|
acceptedStatuses: Array<"invited" | "accepted">;
|
||||||
}) => {
|
}) => {
|
||||||
const membershipOrg = await validateMembershipOrg({
|
const membershipOrg = await validateMembershipOrg({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
organizationId: organization._id,
|
organizationId: organization._id,
|
||||||
acceptedRoles,
|
acceptedRoles,
|
||||||
acceptedStatuses,
|
acceptedStatuses
|
||||||
});
|
});
|
||||||
|
|
||||||
return membershipOrg;
|
return membershipOrg;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export const UpdateMyMfaEnabledV2 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
isMfaEnabled: z.boolean()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateNameV2 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
firstName: z.string().trim(),
|
||||||
|
lastName: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateAuthMethodsV2 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
authMethods: z.nativeEnum(AuthMethod).array().min(1)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CreateApiKeyV2 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
name: z.string().trim(),
|
||||||
|
expiresIn: z.number()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteApiKeyV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
apiKeyDataId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const CreateWebhookV1 = z.object({
|
||||||
|
body: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim(),
|
||||||
|
webhookUrl: z.string().url().trim(),
|
||||||
|
webhookSecretKey: z.string().trim().optional(),
|
||||||
|
secretPath: z.string().trim().default("/")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateWebhookV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
webhookId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
isDisabled: z.boolean().default(false)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const TestWebhookV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
webhookId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteWebhookV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
webhookId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ListWebhooksV1 = z.object({
|
||||||
|
query: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
environment: z.string().trim().optional(),
|
||||||
|
secretPath: z.string().trim().optional()
|
||||||
|
})
|
||||||
|
});
|
||||||
@@ -9,6 +9,7 @@ import { BotService } from "../services";
|
|||||||
import { AuthData } from "../interfaces/middleware";
|
import { AuthData } from "../interfaces/middleware";
|
||||||
import { extractIPDetails } from "../utils/ip";
|
import { extractIPDetails } from "../utils/ip";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { EventType, UserAgentType } from "../ee/models";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate authenticated clients for workspace with id [workspaceId] based
|
* Validate authenticated clients for workspace with id [workspaceId] based
|
||||||
@@ -122,9 +123,241 @@ export const validateClientForWorkspace = async ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CreateWorkspacev1 = z.object({
|
export const GetWorkspaceSecretSnapshotsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
environment: z.string().trim(),
|
||||||
|
folderId: z.string().trim().default("root"),
|
||||||
|
offset: z.number(),
|
||||||
|
limit: z.number()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceSecretSnapshotsCountV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
environment: z.string().trim(),
|
||||||
|
folderId: z.string().trim().default("root")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const RollbackWorkspaceSecretSnapshotV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
environment: z.string().trim(),
|
||||||
|
folderId: z.string().trim().default("root"),
|
||||||
|
version: z.number()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceLogsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
offset: z.number(),
|
||||||
|
limit: z.number(),
|
||||||
|
sortBy: z.string().trim().optional(),
|
||||||
|
userId: z.string().trim().optional(),
|
||||||
|
actionNames: z.string().trim().optional()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceAuditLogsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
eventType: z.nativeEnum(EventType).nullable().optional(),
|
||||||
|
userAgentType: z.nativeEnum(UserAgentType).nullable().optional(),
|
||||||
|
startDate: z.string().datetime().nullable().optional(),
|
||||||
|
endDate: z.string().datetime().nullable().optional(),
|
||||||
|
offset: z.number(),
|
||||||
|
limit: z.number(),
|
||||||
|
actor: z.string().nullish().optional()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceAuditLogActorFilterOptsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceTrustedIpsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const AddWorkspaceTrustedIpV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
ipAddress: z.string().trim(),
|
||||||
|
comment: z.string().trim().default(""),
|
||||||
|
isActive: z.boolean()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateWorkspaceTrustedIpV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
trustedIpId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
ipAddress: z.string().trim(),
|
||||||
|
comment: z.string().trim().default("")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteWorkspaceTrustedIpV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
trustedIpId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspacePublicKeysV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceMembershipsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CreateWorkspaceV1 = z.object({
|
||||||
body: z.object({
|
body: z.object({
|
||||||
workspaceName: z.string().trim(),
|
workspaceName: z.string().trim(),
|
||||||
organizationId: z.string().trim()
|
organizationId: z.string().trim()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const DeleteWorkspaceV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChangeWorkspaceNameV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
name: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const InviteUserToWorkspaceV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
email: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceIntegrationsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceIntegrationAuthorizationsV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceServiceTokensV1 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceServiceTokenDataV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceKeyV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceMembershipsV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateWorkspaceMembershipsV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
membershipId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
role: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteWorkspaceMembershipsV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim(),
|
||||||
|
membershipId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ToggleAutoCapitalizationV2 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
autoCapitalization: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceBlinkIndexStatusV3 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetWorkspaceSecretsV3 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NameWorkspaceSecretsV3 = z.object({
|
||||||
|
params: z.object({
|
||||||
|
workspaceId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
secretsToUpdate: z
|
||||||
|
.object({
|
||||||
|
secretName: z.string().trim(),
|
||||||
|
_id: z.string().trim()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user