Merge branch 'main' into add-bull-queue

add-bull-queue
Maidul Islam 9 months ago committed by GitHub
commit ec234e198a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -34,7 +34,7 @@
<img src="https://img.shields.io/github/commit-activity/m/infisical/infisical" alt="git commit activity" />
</a>
<a href="https://cloudsmith.io/~infisical/repos/">
<img src="https://img.shields.io/badge/Downloads-395.8k-orange" alt="Cloudsmith downloads" />
<img src="https://img.shields.io/badge/Downloads-821.8k-orange" alt="Cloudsmith downloads" />
</a>
<a href="https://infisical.com/slack">
<img src="https://img.shields.io/badge/chat-on%20Slack-blueviolet" alt="Slack community channel" />
@ -116,7 +116,7 @@ Lean about Infisical's code scanning feature [here](https://infisical.com/docs/c
This repo available under the [MIT expat license](https://github.com/Infisical/infisical/blob/main/LICENSE), with the exception of the `ee` directory which will contain premium enterprise features requiring a Infisical license.
If you are interested in managed Infisical Cloud of self-hosted Enterprise Offering, take a look at [our webiste](https://infisical.com/) or [book a meeting with us](https://cal.com/vmatsiiako/infisical-demo):
If you are interested in managed Infisical Cloud of self-hosted Enterprise Offering, take a look at [our website](https://infisical.com/) or [book a meeting with us](https://cal.com/vmatsiiako/infisical-demo):
<a href="https://cal.com/vmatsiiako/infisical-demo"><img alt="Schedule a meeting" src="https://cal.com/book-with-cal-dark.svg" /></a>

@ -14,6 +14,8 @@ FROM node:16-alpine
WORKDIR /app
ENV npm_config_cache /home/node/.npm
COPY package*.json ./
RUN npm ci --only-production
@ -28,4 +30,4 @@ HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \
EXPOSE 4000
CMD ["npm", "run", "start"]
CMD ["npm", "run", "start"]

@ -15,20 +15,20 @@ import { checkUserDevice } from "../../helpers/user";
import {
ACTION_LOGIN,
ACTION_LOGOUT,
AUTH_MODE_JWT,
} from "../../variables";
import {
BadRequestError,
UnauthorizedRequestError,
} from "../../utils/errors";
import { EELogService } from "../../ee/services";
import { getChannelFromUserAgent } from "../../utils/posthog";
import { getUserAgentType } from "../../utils/posthog";
import {
getHttpsEnabled,
getJwtAuthLifetime,
getJwtAuthSecret,
getJwtRefreshSecret,
} from "../../config";
import { ActorType } from "../../ee/models";
declare module "jsonwebtoken" {
export interface UserIDJwtPayload extends jwt.JwtPayload {
@ -142,7 +142,7 @@ export const login2 = async (req: Request, res: Response) => {
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers["user-agent"]),
channel: getUserAgentType(req.headers["user-agent"]),
ipAddress: req.realIP,
});
@ -170,7 +170,7 @@ export const login2 = async (req: Request, res: Response) => {
* @returns
*/
export const logout = async (req: Request, res: Response) => {
if (req.authData.authMode === AUTH_MODE_JWT && req.authData.authPayload instanceof User && req.authData.tokenVersionId) {
if (req.authData.actor.type === ActorType.USER && req.authData.tokenVersionId) {
await clearTokens(req.authData.tokenVersionId)
}
@ -190,7 +190,7 @@ export const logout = async (req: Request, res: Response) => {
logoutAction && await EELogService.createLog({
userId: req.user._id,
actions: [logoutAction],
channel: getChannelFromUserAgent(req.headers["user-agent"]),
channel: getUserAgentType(req.headers["user-agent"]),
ipAddress: req.realIP,
});

@ -3,7 +3,9 @@ import { Types } from "mongoose";
import { standardRequest } from "../../config/request";
import { getApps, getTeams, revokeAccess } from "../../integrations";
import { Bot, IntegrationAuth } from "../../models";
import { EventType } from "../../ee/models";
import { IntegrationService } from "../../services";
import { EEAuditLogService } from "../../ee/services";
import {
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
@ -62,6 +64,19 @@ export const oAuthExchange = async (req: Request, res: Response) => {
environment: environments[0].slug
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.AUTHORIZE_INTEGRATION,
metadata: {
integration: integrationAuth.integration
}
},
{
workspaceId: integrationAuth.workspace
}
);
return res.status(200).send({
integrationAuth
});
@ -129,6 +144,19 @@ export const saveIntegrationAccessToken = async (req: Request, res: Response) =>
});
if (!integrationAuth) throw new Error("Failed to save integration access token");
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.AUTHORIZE_INTEGRATION,
metadata: {
integration: integrationAuth.integration
}
},
{
workspaceId: integrationAuth.workspace
}
);
return res.status(200).send({
integrationAuth
@ -530,6 +558,23 @@ export const deleteIntegrationAuth = async (req: Request, res: Response) => {
integrationAuth: req.integrationAuth,
accessToken: req.accessToken
});
if (!integrationAuth) return res.status(400).send({
message: "Failed to find integration authorization"
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UNAUTHORIZE_INTEGRATION,
metadata: {
integration: integrationAuth.integration
}
},
{
workspaceId: integrationAuth.workspace
}
);
return res.status(200).send({
integrationAuth

@ -6,6 +6,8 @@ import { eventStartIntegration } from "../../events";
import Folder from "../../models/folder";
import { getFolderByPath } from "../../services/FolderService";
import { BadRequestError } from "../../utils/errors";
import { EEAuditLogService } from "../../ee/services";
import { EventType } from "../../ee/models";
/**
* Create/initialize an (empty) integration for integration authorization
@ -74,6 +76,31 @@ export const createIntegration = async (req: Request, res: Response) => {
})
});
}
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_INTEGRATION,
metadata: {
integrationId: integration._id.toString(),
integration: integration.integration,
environment: integration.environment,
secretPath,
url: integration.url,
app: integration.app,
appId: integration.appId,
targetEnvironment: integration.targetEnvironment,
targetEnvironmentId: integration.targetEnvironmentId,
targetService: integration.targetService,
targetServiceId: integration.targetServiceId,
path: integration.path,
region: integration.region
}
},
{
workspaceId: integration.workspace
}
);
return res.status(200).send({
integration
@ -148,8 +175,7 @@ export const updateIntegration = async (req: Request, res: Response) => {
};
/**
* Delete integration with id [integrationId] and deactivate bot if there are
* no integrations left
* Delete integration with id [integrationId]
* @param req
* @param res
* @returns
@ -163,6 +189,31 @@ export const deleteIntegration = async (req: Request, res: Response) => {
if (!integration) throw new Error("Failed to find integration");
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.DELETE_INTEGRATION,
metadata: {
integrationId: integration._id.toString(),
integration: integration.integration,
environment: integration.environment,
secretPath: integration.secretPath,
url: integration.url,
app: integration.app,
appId: integration.appId,
targetEnvironment: integration.targetEnvironment,
targetEnvironmentId: integration.targetEnvironmentId,
targetService: integration.targetService,
targetServiceId: integration.targetServiceId,
path: integration.path,
region: integration.region
}
},
{
workspaceId: integration.workspace
}
);
return res.status(200).send({
integration
});

@ -1,6 +1,9 @@
import { Types } from "mongoose";
import { Request, Response } from "express";
import { Key } from "../../models";
import { findMembership } from "../../helpers/membership";
import { EventType } from "../../ee/models";
import { EEAuditLogService } from "../../ee/services";
/**
* Add (encrypted) copy of workspace key for workspace with id [workspaceId] for user with
@ -44,7 +47,7 @@ export const uploadKey = async (req: Request, res: Response) => {
*/
export const getLatestKey = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
// get latest key
const latestKey = await Key.find({
workspace: workspaceId,
@ -58,6 +61,18 @@ export const getLatestKey = async (req: Request, res: Response) => {
if (latestKey.length > 0) {
resObj["latestKey"] = latestKey[0];
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.GET_WORKSPACE_KEY,
metadata: {
keyId: latestKey[0]._id.toString()
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
}
);
}
return res.status(200).send(resObj);

@ -1,9 +1,12 @@
import { Request, Response } from "express";
import { Key, Membership, MembershipOrg, User } from "../../models";
import { Types } from "mongoose";
import { IUser, Key, Membership, MembershipOrg, User } from "../../models";
import { EventType } from "../../ee/models";
import { deleteMembership as deleteMember, findMembership } from "../../helpers/membership";
import { sendMail } from "../../helpers/nodemailer";
import { ACCEPTED, ADMIN, MEMBER } from "../../variables";
import { getSiteURL } from "../../config";
import { EEAuditLogService } from "../../ee/services";
/**
* Check that user is a member of workspace with id [workspaceId]
@ -36,11 +39,11 @@ export const validateMembership = async (req: Request, res: Response) => {
*/
export const deleteMembership = async (req: Request, res: Response) => {
const { membershipId } = req.params;
// check if membership to delete exists
const membershipToDelete = await Membership.findOne({
_id: membershipId
}).populate("user");
}).populate<{ user: IUser }>("user");
if (!membershipToDelete) {
throw new Error("Failed to delete workspace membership that doesn't exist");
@ -66,6 +69,20 @@ export const deleteMembership = async (req: Request, res: Response) => {
const deletedMembership = await deleteMember({
membershipId: membershipToDelete._id.toString()
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.REMOVE_WORKSPACE_MEMBER,
metadata: {
userId: membershipToDelete.user._id.toString(),
email: membershipToDelete.user.email
}
},
{
workspaceId: membership.workspace
}
);
return res.status(200).send({
deletedMembership
@ -87,9 +104,9 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
}
// validate target membership
const membershipToChangeRole = await findMembership({
_id: membershipId
});
const membershipToChangeRole = await Membership
.findById(membershipId)
.populate<{ user: IUser }>("user");
if (!membershipToChangeRole) {
throw new Error("Failed to find membership to change role");
@ -110,9 +127,27 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
// user is not an admin member of the workspace
throw new Error("Insufficient role for changing member roles");
}
const oldRole = membershipToChangeRole.role;
membershipToChangeRole.role = role;
await membershipToChangeRole.save();
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UPDATE_USER_WORKSPACE_ROLE,
metadata: {
userId: membershipToChangeRole.user._id.toString(),
email: membershipToChangeRole.user.email,
oldRole,
newRole: membershipToChangeRole.role
}
},
{
workspaceId: membershipToChangeRole.workspace
}
);
return res.status(200).send({
membership: membershipToChangeRole
@ -140,7 +175,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
const inviteeMembership = await Membership.findOne({
user: invitee._id,
workspace: workspaceId
});
}).populate<{ user: IUser }>("user");
if (inviteeMembership) throw new Error("Failed to add existing member of workspace");
@ -181,6 +216,20 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
}
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.ADD_WORKSPACE_MEMBER,
metadata: {
userId: invitee._id.toString(),
email: invitee.email
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
}
);
return res.status(200).send({
invitee,
latestKey

@ -103,14 +103,14 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
// validate membership
const membershipOrg = await MembershipOrg.findOne({
user: req.user._id,
organization: organizationId
organization: new Types.ObjectId(organizationId)
});
if (!membershipOrg) {
throw new Error("Failed to validate organization membership");
}
const plan = await EELicenseService.getPlan(organizationId);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId));
const ssoConfig = await SSOConfig.findOne({
organization: new Types.ObjectId(organizationId)

@ -260,22 +260,6 @@ export const createOrganizationPortalSession = async (
}
};
/**
* Return organization subscriptions
* @param req
* @param res
* @returns
*/
export const getOrganizationSubscriptions = async (
req: Request,
res: Response
) => {
return res.status(200).send({
subscriptions: []
});
};
/**
* Given a org id, return the projects each member of the org belongs to
* @param req

@ -5,7 +5,7 @@ import * as bigintConversion from "bigint-conversion";
import { BackupPrivateKey, LoginSRPDetail, User } from "../../models";
import { clearTokens, createToken, sendMail } from "../../helpers";
import { TokenService } from "../../services";
import { AUTH_MODE_JWT, TOKEN_EMAIL_PASSWORD_RESET } from "../../variables";
import { TOKEN_EMAIL_PASSWORD_RESET } from "../../variables";
import { BadRequestError } from "../../utils/errors";
import {
getHttpsEnabled,
@ -13,6 +13,7 @@ import {
getJwtSignupSecret,
getSiteURL
} from "../../config";
import { ActorType } from "../../ee/models";
/**
* Password reset step 1: Send email verification link to email [email]
@ -208,8 +209,7 @@ export const changePassword = async (req: Request, res: Response) => {
);
if (
req.authData.authMode === AUTH_MODE_JWT &&
req.authData.authPayload instanceof User &&
req.authData.actor.type === ActorType.USER &&
req.authData.tokenVersionId
) {
await clearTokens(req.authData.tokenVersionId);

@ -1,17 +1,49 @@
import { Request, Response } from "express";
import { validateMembership } from "../../helpers";
import { isValidScope, validateMembership } from "../../helpers";
import { ServiceTokenData } from "../../models";
import Folder from "../../models/folder";
import SecretImport from "../../models/secretImports";
import { getAllImportedSecrets } from "../../services/SecretImportService";
import { BadRequestError } from "../../utils/errors";
import { getFolderWithPathFromId } from "../../services/FolderService";
import { BadRequestError, ResourceNotFoundError,UnauthorizedRequestError } from "../../utils/errors";
import { ADMIN, MEMBER } from "../../variables";
import { EEAuditLogService } from "../../ee/services";
import { EventType } from "../../ee/models";
export const createSecretImport = async (req: Request, res: Response) => {
const { workspaceId, environment, folderId, secretImport } = req.body;
const folders = await Folder.findOne({
workspace: workspaceId,
environment
}).lean();
if (!folders && folderId !== "root") {
throw ResourceNotFoundError({
message: "Failed to find folder"
});
}
let secretPath = "/";
if (folders) {
const { folderPath } = getFolderWithPathFromId(folders.nodes, folderId);
secretPath = folderPath;
}
if (req.authData.authPayload instanceof ServiceTokenData) {
// root check
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, secretPath);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
const importSecDoc = await SecretImport.findOne({
workspace: workspaceId,
environment,
folderId
});
const importToSecretPath = folders?getFolderWithPathFromId(folders.nodes, folderId).folderPath:"/";
if (!importSecDoc) {
const doc = new SecretImport({
@ -20,7 +52,25 @@ export const createSecretImport = async (req: Request, res: Response) => {
folderId,
imports: [{ environment: secretImport.environment, secretPath: secretImport.secretPath }]
});
await doc.save();
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_SECRET_IMPORT,
metadata: {
secretImportId: doc._id.toString(),
folderId: doc.folderId.toString(),
importFromEnvironment: secretImport.environment,
importFromSecretPath: secretImport.secretPath,
importToEnvironment: environment,
importToSecretPath
}
},
{
workspaceId: doc.workspace
}
);
return res.status(200).json({ message: "successfully created secret import" });
}
@ -30,11 +80,30 @@ export const createSecretImport = async (req: Request, res: Response) => {
if (doesImportExist) {
throw BadRequestError({ message: "Secret import already exist" });
}
importSecDoc.imports.push({
environment: secretImport.environment,
secretPath: secretImport.secretPath
});
await importSecDoc.save();
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_SECRET_IMPORT,
metadata: {
secretImportId: importSecDoc._id.toString(),
folderId: importSecDoc.folderId.toString(),
importFromEnvironment: secretImport.environment,
importFromSecretPath: secretImport.secretPath,
importToEnvironment: environment,
importToSecretPath
}
},
{
workspaceId: importSecDoc.workspace
}
);
return res.status(200).json({ message: "successfully created secret import" });
};
@ -48,14 +117,68 @@ export const updateSecretImport = async (req: Request, res: Response) => {
throw BadRequestError({ message: "Import not found" });
}
await validateMembership({
userId: req.user._id.toString(),
workspaceId: importSecDoc.workspace,
acceptedRoles: [ADMIN, MEMBER]
});
if (!(req.authData.authPayload instanceof ServiceTokenData)) {
await validateMembership({
userId: req.user._id.toString(),
workspaceId: importSecDoc.workspace,
acceptedRoles: [ADMIN, MEMBER]
});
} else {
// 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(
req.authData.authPayload,
importSecDoc.environment,
secretPath
);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
const orderBefore = importSecDoc.imports;
importSecDoc.imports = secretImports;
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(
req.authData,
{
type: EventType.UPDATE_SECRET_IMPORT,
metadata: {
importToEnvironment: importSecDoc.environment,
importToSecretPath,
secretImportId: importSecDoc._id.toString(),
folderId: importSecDoc.folderId.toString(),
orderBefore,
orderAfter: secretImports
}
},
{
workspaceId: importSecDoc.workspace
}
);
return res.status(200).json({ message: "successfully updated secret import" });
};
@ -67,16 +190,69 @@ export const deleteSecretImport = async (req: Request, res: Response) => {
throw BadRequestError({ message: "Import not found" });
}
await validateMembership({
userId: req.user._id.toString(),
workspaceId: importSecDoc.workspace,
acceptedRoles: [ADMIN, MEMBER]
});
if (!(req.authData.authPayload instanceof ServiceTokenData)) {
await validateMembership({
userId: req.user._id.toString(),
workspaceId: importSecDoc.workspace,
acceptedRoles: [ADMIN, MEMBER]
});
} else {
// 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(
req.authData.authPayload,
importSecDoc.environment,
secretPath
);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
importSecDoc.imports = importSecDoc.imports.filter(
({ environment, secretPath }) =>
!(environment === secretImportEnv && secretPath === secretImportPath)
);
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(
req.authData,
{
type: EventType.DELETE_SECRET_IMPORT,
metadata: {
secretImportId: importSecDoc._id.toString(),
folderId: importSecDoc.folderId.toString(),
importFromEnvironment: secretImportEnv,
importFromSecretPath: secretImportPath,
importToEnvironment: importSecDoc.environment,
importToSecretPath
}
},
{
workspaceId: importSecDoc.workspace
}
);
return res.status(200).json({ message: "successfully delete secret import" });
};
@ -92,6 +268,29 @@ export const getSecretImports = async (req: Request, res: Response) => {
return res.status(200).json({ secretImport: {} });
}
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(
req.authData.authPayload,
importSecDoc.environment,
secretPath
);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
return res.status(200).json({ secretImport: importSecDoc });
};
@ -111,6 +310,45 @@ export const getAllSecretsFromImport = async (req: Request, res: Response) => {
return res.status(200).json({ secrets: [] });
}
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(
req.authData.authPayload,
importSecDoc.environment,
secretPath
);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.GET_SECRET_IMPORTS,
metadata: {
environment,
secretImportId: importSecDoc._id.toString(),
folderId,
numberOfImports: importSecDoc.imports.length
}
},
{
workspaceId: importSecDoc.workspace
}
);
const secrets = await getAllImportedSecrets(workspaceId, environment, folderId);
return res.status(200).json({ secrets });
};

@ -1,39 +1,49 @@
import { Request, Response } from "express";
import { Secret } from "../../models";
import { Types } from "mongoose";
import { EventType, FolderVersion } from "../../ee/models";
import { EEAuditLogService, EESecretService } from "../../ee/services";
import { validateMembership } from "../../helpers/membership";
import { isValidScope } from "../../helpers/secrets";
import { Secret, ServiceTokenData } from "../../models";
import Folder from "../../models/folder";
import { BadRequestError } from "../../utils/errors";
import {
appendFolder,
deleteFolderById,
generateFolderId,
getAllFolderIds,
getFolderByPath,
getFolderWithPathFromId,
getParentFromFolderId,
searchByFolderId,
searchByFolderIdWithDir,
validateFolderName,
validateFolderName
} from "../../services/FolderService";
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
import { ADMIN, MEMBER } from "../../variables";
import { validateMembership } from "../../helpers/membership";
import { FolderVersion } from "../../ee/models";
import { EESecretService } from "../../ee/services";
// TODO
// verify workspace id/environment
export const createFolder = async (req: Request, res: Response) => {
const { workspaceId, environment, folderName, parentFolderId } = req.body;
if (!validateFolderName(folderName)) {
throw BadRequestError({
message: "Folder name cannot contain spaces. Only underscore and dashes",
message: "Folder name cannot contain spaces. Only underscore and dashes"
});
}
const folders = await Folder.findOne({
workspace: workspaceId,
environment,
environment
}).lean();
// space has no folders initialized
if (!folders) {
if (req.authData.authPayload instanceof ServiceTokenData) {
// root check
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, "/");
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
const id = generateFolderId();
const folder = new Folder({
workspace: workspaceId,
@ -42,39 +52,93 @@ export const createFolder = async (req: Request, res: Response) => {
id: "root",
name: "root",
version: 1,
children: [{ id, name: folderName, children: [], version: 1 }],
},
children: [{ id, name: folderName, children: [], version: 1 }]
}
});
await folder.save();
const folderVersion = new FolderVersion({
workspace: workspaceId,
environment,
nodes: folder.nodes,
nodes: folder.nodes
});
await folderVersion.save();
await EESecretService.takeSecretSnapshot({
workspaceId,
environment,
environment
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_FOLDER,
metadata: {
environment,
folderId: id,
folderName,
folderPath: `root/${folderName}`
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
}
);
return res.json({ folder: { id, name: folderName } });
}
const folder = appendFolder(folders.nodes, { folderName, parentFolderId });
await Folder.findByIdAndUpdate(folders._id, folders);
const { folder: parentFolder, folderPath: parentFolderPath } = getFolderWithPathFromId(
folders.nodes,
parentFolderId || "root"
);
if (req.authData.authPayload instanceof ServiceTokenData) {
// root check
const isValidScopeAccess = isValidScope(
req.authData.authPayload,
environment,
parentFolderPath
);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
await Folder.findByIdAndUpdate(folders._id, folders);
const parentFolder = searchByFolderId(folders.nodes, parentFolderId);
const folderVersion = new FolderVersion({
workspace: workspaceId,
environment,
nodes: parentFolder,
nodes: parentFolder
});
await folderVersion.save();
await EESecretService.takeSecretSnapshot({
workspaceId,
environment,
folderId: parentFolderId,
folderId: parentFolderId
});
const {folderPath} = getFolderWithPathFromId(folders.nodes, folder.id);
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_FOLDER,
metadata: {
environment,
folderId: folder.id,
folderName,
folderPath
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
}
);
return res.json({ folder });
};
@ -82,28 +146,46 @@ export const createFolder = async (req: Request, res: Response) => {
export const updateFolderById = async (req: Request, res: Response) => {
const { folderId } = req.params;
const { name, workspaceId, environment } = req.body;
if (!validateFolderName(name)) {
throw BadRequestError({
message: "Folder name cannot contain spaces. Only underscore and dashes"
});
}
const folders = await Folder.findOne({ workspace: workspaceId, environment });
if (!folders) {
throw BadRequestError({ message: "The folder doesn't exist" });
}
// check that user is a member of the workspace
await validateMembership({
userId: req.user._id.toString(),
workspaceId,
acceptedRoles: [ADMIN, MEMBER],
});
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);
if (!parentFolder) {
throw BadRequestError({ message: "The folder doesn't exist" });
}
const folder = parentFolder.children.find(({ id }) => id === folderId);
if (!folder) {
throw BadRequestError({ message: "The folder doesn't exist" });
}
if (req.authData.authPayload instanceof ServiceTokenData) {
const { folderPath: secretPath } = getFolderWithPathFromId(folders.nodes, parentFolder.id);
// root check
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, secretPath);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
const oldFolderName = folder.name;
parentFolder.version += 1;
folder.name = name;
@ -111,19 +193,38 @@ export const updateFolderById = async (req: Request, res: Response) => {
const folderVersion = new FolderVersion({
workspace: workspaceId,
environment,
nodes: parentFolder,
nodes: parentFolder
});
await folderVersion.save();
await EESecretService.takeSecretSnapshot({
workspaceId,
environment,
folderId: parentFolder.id,
folderId: parentFolder.id
});
const {folderPath} = getFolderWithPathFromId(folders.nodes, folder.id);
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UPDATE_FOLDER,
metadata: {
environment,
folderId: folder.id,
oldFolderName,
newFolderName: name,
folderPath
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
}
);
return res.json({
message: "Successfully updated folder",
folder: { name: folder.name, id: folder.id },
folder: { name: folder.name, id: folder.id }
});
};
@ -136,12 +237,16 @@ export const deleteFolder = async (req: Request, res: Response) => {
throw BadRequestError({ message: "The folder doesn't exist" });
}
// check that user is a member of the workspace
await validateMembership({
userId: req.user._id.toString(),
workspaceId,
acceptedRoles: [ADMIN, MEMBER],
});
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);
if (!delOp) {
@ -149,6 +254,14 @@ export const deleteFolder = async (req: Request, res: Response) => {
}
const { deletedNode: delFolder, parent: parentFolder } = delOp;
if (req.authData.authPayload instanceof ServiceTokenData) {
const { folderPath: secretPath } = getFolderWithPathFromId(folders.nodes, parentFolder.id);
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, secretPath);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
parentFolder.version += 1;
const delFolderIds = getAllFolderIds(delFolder);
@ -156,35 +269,50 @@ export const deleteFolder = async (req: Request, res: Response) => {
const folderVersion = new FolderVersion({
workspace: workspaceId,
environment,
nodes: parentFolder,
nodes: parentFolder
});
await folderVersion.save();
if (delFolderIds.length) {
await Secret.deleteMany({
folder: { $in: delFolderIds.map(({ id }) => id) },
workspace: workspaceId,
environment,
environment
});
}
await EESecretService.takeSecretSnapshot({
workspaceId,
environment,
folderId: parentFolder.id,
folderId: parentFolder.id
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.DELETE_FOLDER ,
metadata: {
environment,
folderId,
folderName: delFolder.name,
folderPath
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
}
);
res.send({ message: "successfully deleted folders", folders: delFolderIds });
};
// TODO: validate workspace
export const getFolders = async (req: Request, res: Response) => {
const { workspaceId, environment, parentFolderId, parentFolderPath } =
req.query as {
workspaceId: string;
environment: string;
parentFolderId?: string;
parentFolderPath?: string;
};
const { workspaceId, environment, parentFolderId, parentFolderPath } = req.query as {
workspaceId: string;
environment: string;
parentFolderId?: string;
parentFolderPath?: string;
};
const folders = await Folder.findOne({ workspace: workspaceId, environment });
if (!folders) {
@ -192,16 +320,29 @@ export const getFolders = async (req: Request, res: Response) => {
return;
}
// check that user is a member of the workspace
await validateMembership({
userId: req.user._id.toString(),
workspaceId,
acceptedRoles: [ADMIN, MEMBER],
});
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 (parentFolderPath) {
if (req.authData.authPayload instanceof ServiceTokenData) {
const isValidScopeAccess = isValidScope(
req.authData.authPayload,
environment,
parentFolderPath
);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
const folder = getFolderByPath(folders.nodes, parentFolderPath);
if (!folder) {
res.send({ folders: [], dir: [] });
return;
@ -209,27 +350,36 @@ export const getFolders = async (req: Request, res: Response) => {
// dir is not needed at present as this is only used in overview section of secrets
res.send({
folders: folder.children.map(({ id, name }) => ({ id, name })),
dir: [{ name: folder.name, id: folder.id }],
dir: [{ name: folder.name, id: folder.id }]
});
}
if (!parentFolderId) {
if (req.authData.authPayload instanceof ServiceTokenData) {
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, "/");
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
const rootFolders = folders.nodes.children.map(({ id, name }) => ({
id,
name,
name
}));
res.send({ folders: rootFolders });
return;
}
const folderBySearch = searchByFolderIdWithDir(folders.nodes, parentFolderId);
if (!folderBySearch) {
throw BadRequestError({ message: "The folder doesn't exist" });
const { folder, folderPath, dir } = getFolderWithPathFromId(folders.nodes, parentFolderId);
if (req.authData.authPayload instanceof ServiceTokenData) {
const isValidScopeAccess = isValidScope(req.authData.authPayload, environment, folderPath);
if (!isValidScopeAccess) {
throw UnauthorizedRequestError({ message: "Folder Permission Denied" });
}
}
const { folder, dir } = folderBySearch;
res.send({
folders: folder.children.map(({ id, name }) => ({ id, name })),
dir,
dir
});
};

@ -1,5 +1,5 @@
import { Request, Response } from "express";
import { User } from "../../models";
import { AuthMethod, User } from "../../models";
import { checkEmailVerification, sendEmailVerification } from "../../helpers/signup";
import { createToken } from "../../helpers/auth";
import { BadRequestError } from "../../utils/errors";
@ -81,7 +81,8 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
if (!user) {
user = await new User({
email
email,
authMethods: [AuthMethod.EMAIL]
}).save();
}

@ -4,7 +4,9 @@ import { client, getRootEncryptionKey } from "../../config";
import { validateMembership } from "../../helpers";
import Webhook from "../../models/webhooks";
import { getWebhookPayload, triggerWebhookRequest } from "../../services/WebhookService";
import { BadRequestError } from "../../utils/errors";
import { BadRequestError, ResourceNotFoundError } from "../../utils/errors";
import { EEAuditLogService } from "../../ee/services";
import { EventType } from "../../ee/models";
import { ADMIN, ALGORITHM_AES_256_GCM, ENCODING_SCHEME_BASE64, MEMBER } from "../../variables";
export const createWebhook = async (req: Request, res: Response) => {
@ -27,6 +29,23 @@ export const createWebhook = async (req: Request, res: Response) => {
}
await webhook.save();
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_WEBHOOK,
metadata: {
webhookId: webhook._id.toString(),
environment,
secretPath,
webhookUrl,
isDisabled: false
}
},
{
workspaceId
}
);
return res.status(200).send({
webhook,
@ -54,6 +73,23 @@ export const updateWebhook = async (req: Request, res: Response) => {
}
await webhook.save();
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UPDATE_WEBHOOK_STATUS,
metadata: {
webhookId: webhook._id.toString(),
environment: webhook.environment,
secretPath: webhook.secretPath,
webhookUrl: webhook.url,
isDisabled
}
},
{
workspaceId: webhook.workspace
}
);
return res.status(200).send({
webhook,
message: "successfully updated webhook"
@ -62,9 +98,10 @@ export const updateWebhook = async (req: Request, res: Response) => {
export const deleteWebhook = async (req: Request, res: Response) => {
const { webhookId } = req.params;
const webhook = await Webhook.findById(webhookId);
let webhook = await Webhook.findById(webhookId);
if (!webhook) {
throw BadRequestError({ message: "Webhook not found!!" });
throw ResourceNotFoundError({ message: "Webhook not found!!" });
}
await validateMembership({
@ -72,8 +109,29 @@ export const deleteWebhook = async (req: Request, res: Response) => {
workspaceId: webhook.workspace,
acceptedRoles: [ADMIN, MEMBER]
});
webhook = await Webhook.findByIdAndDelete(webhookId);
if (!webhook) {
throw ResourceNotFoundError({ message: "Webhook not found!!" });
}
await webhook.deleteOne();
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.DELETE_WEBHOOK,
metadata: {
webhookId: webhook._id.toString(),
environment: webhook.environment,
secretPath: webhook.secretPath,
webhookUrl: webhook.url,
isDisabled: webhook.isDisabled
}
},
{
workspaceId: webhook.workspace
}
);
return res.status(200).send({
message: "successfully removed webhook"

@ -1,3 +1,4 @@
import { Types } from "mongoose";
import { Request, Response } from "express";
import {
IUser,
@ -108,14 +109,14 @@ export const createWorkspace = async (req: Request, res: Response) => {
// validate organization membership
const membershipOrg = await MembershipOrg.findOne({
user: req.user._id,
organization: organizationId,
organization: new Types.ObjectId(organizationId),
});
if (!membershipOrg) {
throw new Error("Failed to validate organization membership");
}
const plan = await EELicenseService.getPlan(organizationId);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId));
if (plan.workspaceLimit !== null) {
// case: limit imposed on number of workspaces allowed
@ -134,7 +135,7 @@ export const createWorkspace = async (req: Request, res: Response) => {
// create workspace and add user as member
const workspace = await create({
name: workspaceName,
organizationId,
organizationId: new Types.ObjectId(organizationId),
});
await addMemberships({

@ -14,7 +14,7 @@ import {
ACTION_LOGIN,
TOKEN_EMAIL_MFA,
} from "../../variables";
import { getChannelFromUserAgent } from "../../utils/posthog"; // TODO: move this
import { getUserAgentType } from "../../utils/posthog"; // TODO: move this
import {
getHttpsEnabled,
getJwtMfaLifetime,
@ -203,7 +203,7 @@ export const login2 = async (req: Request, res: Response) => {
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers["user-agent"]),
channel: getUserAgentType(req.headers["user-agent"]),
ipAddress: req.ip,
});
@ -336,7 +336,7 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers["user-agent"]),
channel: getUserAgentType(req.headers["user-agent"]),
ipAddress: req.realIP,
});

@ -1,4 +1,5 @@
import { Request, Response } from "express";
import { Types } from "mongoose";
import {
Integration,
Membership,
@ -7,8 +8,8 @@ import {
ServiceTokenData,
Workspace,
} from "../../models";
import { SecretVersion } from "../../ee/models";
import { EELicenseService } from "../../ee/services";
import { EventType, SecretVersion } from "../../ee/models";
import { EEAuditLogService, EELicenseService } from "../../ee/services";
import { BadRequestError, WorkspaceNotFoundError } from "../../utils/errors";
import _ from "lodash";
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables";
@ -30,7 +31,7 @@ export const createWorkspaceEnvironment = async (
if (!workspace) throw WorkspaceNotFoundError();
const plan = await EELicenseService.getPlan(workspace.organization.toString());
const plan = await EELicenseService.getPlan(workspace.organization);
if (plan.environmentLimit !== null) {
// case: limit imposed on number of environments allowed
@ -58,7 +59,21 @@ export const createWorkspaceEnvironment = async (
});
await workspace.save();
await EELicenseService.refreshPlan(workspace.organization.toString(), workspaceId);
await EELicenseService.refreshPlan(workspace.organization, new Types.ObjectId(workspaceId));
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_ENVIRONMENT,
metadata: {
name: environmentName,
slug: environmentSlug
}
},
{
workspaceId: workspace._id
}
);
return res.status(200).send({
message: "Successfully created new environment",
@ -70,6 +85,43 @@ export const createWorkspaceEnvironment = async (
});
};
/**
* Swaps the ordering of two environments in the database. This is purely for aesthetic purposes.
* @param req
* @param res
* @returns
*/
export const reorderWorkspaceEnvironments = async (
req: Request,
res: Response
) => {
const { workspaceId } = req.params;
const { environmentSlug, environmentName, otherEnvironmentSlug, otherEnvironmentName } = req.body;
// atomic update the env to avoid conflict
const workspace = await Workspace.findById(workspaceId).exec();
if (!workspace) {
throw BadRequestError({message: "Couldn't load workspace"});
}
const environmentIndex = workspace.environments.findIndex((env) => env.name === environmentName && env.slug === environmentSlug)
const otherEnvironmentIndex = workspace.environments.findIndex((env) => env.name === otherEnvironmentName && env.slug === otherEnvironmentSlug)
if (environmentIndex === -1 || otherEnvironmentIndex === -1) {
throw BadRequestError({message: "environment or otherEnvironment couldn't be found"})
}
// swap the order of the environments
[workspace.environments[environmentIndex], workspace.environments[otherEnvironmentIndex]] = [workspace.environments[otherEnvironmentIndex], workspace.environments[environmentIndex]]
await workspace.save()
return res.status(200).send({
message: "Successfully reordered environments",
workspace: workspaceId,
});
};
/**
* Rename workspace environment with new name and slug of a workspace with [workspaceId]
* Old slug [oldEnvironmentSlug] must be provided
@ -110,6 +162,8 @@ export const renameWorkspaceEnvironment = async (
throw new Error("Invalid environment given");
}
const oldEnvironment = workspace.environments[envIndex];
workspace.environments[envIndex].name = environmentName;
workspace.environments[envIndex].slug = environmentSlug.toLowerCase();
@ -141,8 +195,23 @@ export const renameWorkspaceEnvironment = async (
},
{ $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } },
{ arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] }
)
);
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UPDATE_ENVIRONMENT,
metadata: {
oldName: oldEnvironment.name,
newName: environmentName,
oldSlug: oldEnvironment.slug,
newSlug: environmentSlug.toLowerCase()
}
},
{
workspaceId: workspace._id
}
);
return res.status(200).send({
message: "Successfully update environment",
@ -179,6 +248,8 @@ export const deleteWorkspaceEnvironment = async (
throw new Error("Invalid environment given");
}
const oldEnvironment = workspace.environments[envIndex];
workspace.environments.splice(envIndex, 1);
await workspace.save();
@ -215,7 +286,21 @@ export const deleteWorkspaceEnvironment = async (
{ $pull: { deniedPermissions: { environmentSlug: environmentSlug } } }
);
await EELicenseService.refreshPlan(workspace.organization.toString(), workspaceId);
await EELicenseService.refreshPlan(workspace.organization, new Types.ObjectId(workspaceId));
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.DELETE_ENVIRONMENT,
metadata: {
name: oldEnvironment.name,
slug: oldEnvironment.slug
}
},
{
workspaceId: workspace._id
}
);
return res.status(200).send({
message: "Successfully deleted environment",

@ -1,7 +1,7 @@
import { Types } from "mongoose";
import { Request, Response } from "express";
import { ISecret, Secret, ServiceTokenData } from "../../models";
import { IAction, SecretVersion } from "../../ee/models";
import { AuditLog, EventType, IAction, SecretVersion } from "../../ee/models";
import {
ACTION_ADD_SECRETS,
ACTION_DELETE_SECRETS,
@ -14,9 +14,9 @@ import {
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
import { EventService } from "../../services";
import { eventPushSecrets } from "../../events";
import { EELogService, EESecretService } from "../../ee/services";
import { EEAuditLogService, EELogService, EESecretService } from "../../ee/services";
import { SecretService, TelemetryService } from "../../services";
import { getChannelFromUserAgent } from "../../utils/posthog";
import { getUserAgentType } from "../../utils/posthog";
import { PERMISSION_WRITE_SECRETS } from "../../variables";
import {
userHasNoAbility,
@ -44,7 +44,7 @@ import { getAllImportedSecrets } from "../../services/SecretImportService";
* @param res
*/
export const batchSecrets = async (req: Request, res: Response) => {
const channel = getChannelFromUserAgent(req.headers["user-agent"]);
const channel = getUserAgentType(req.headers["user-agent"]);
const postHogClient = await TelemetryService.getPostHogClient();
const {
@ -56,12 +56,13 @@ export const batchSecrets = async (req: Request, res: Response) => {
environment: string;
requests: BatchSecretRequest[];
} = req.body;
let secretPath = req.body.secretPath as string;
let folderId = req.body.folderId as string;
const createSecrets: BatchSecret[] = [];
const updateSecrets: BatchSecret[] = [];
const deleteSecrets: Types.ObjectId[] = [];
const deleteSecrets: { _id: Types.ObjectId, secretName: string; }[] = [];
const actions: IAction[] = [];
// get secret blind index salt
@ -133,7 +134,7 @@ export const batchSecrets = async (req: Request, res: Response) => {
});
break;
case "DELETE":
deleteSecrets.push(new Types.ObjectId(request.secret._id));
deleteSecrets.push({ _id: new Types.ObjectId(request.secret._id), secretName: request.secret.secretName });
break;
}
}
@ -153,7 +154,31 @@ export const batchSecrets = async (req: Request, res: Response) => {
};
})
});
const auditLogs = await Promise.all(
createdSecrets.map((secret, index) => {
return EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_SECRET,
metadata: {
environment: secret.environment,
secretPath: secretPath ?? "/",
secretId: secret._id.toString(),
secretKey: createSecrets[index].secretName,
secretVersion: secret.version
}
},
{
workspaceId: secret.workspace
},
false
);
})
);
await AuditLog.insertMany(auditLogs);
const addAction = (await EELogService.createAction({
name: ACTION_ADD_SECRETS,
userId: req.user?._id,
@ -252,6 +277,30 @@ export const batchSecrets = async (req: Request, res: Response) => {
$in: updateSecrets.map((u) => new Types.ObjectId(u._id))
}
});
const auditLogs = await Promise.all(
updateSecrets.map((secret) => {
return EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UPDATE_SECRET,
metadata: {
environment,
secretPath: secretPath ?? "/",
secretId: secret._id.toString(),
secretKey: secret.secretName,
secretVersion: listedSecretsObj[secret._id.toString()].version
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
},
false
);
})
);
await AuditLog.insertMany(auditLogs);
const updateAction = (await EELogService.createAction({
name: ACTION_UPDATE_SECRETS,
@ -279,21 +328,60 @@ export const batchSecrets = async (req: Request, res: Response) => {
// handle delete secrets
if (deleteSecrets.length > 0) {
const deleteSecretIds: Types.ObjectId[] = deleteSecrets.map((s) => s._id);
const deletedSecretsObj = (await Secret.find({
_id: {
$in: deleteSecretIds
}
}))
.reduce(
(obj: any, secret: ISecret) => ({
...obj,
[secret._id.toString()]: secret
}),
{}
);
await Secret.deleteMany({
_id: {
$in: deleteSecrets
$in: deleteSecretIds
}
});
await EESecretService.markDeletedSecretVersions({
secretIds: deleteSecrets
secretIds: deleteSecretIds
});
const auditLogs = await Promise.all(
deleteSecrets.map((secret) => {
return EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.DELETE_SECRET,
metadata: {
environment,
secretPath: secretPath ?? "/",
secretId: secret._id.toString(),
secretKey: secret.secretName,
secretVersion: deletedSecretsObj[secret._id.toString()].version
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
},
false
);
})
);
await AuditLog.insertMany(auditLogs);
const deleteAction = (await EELogService.createAction({
name: ACTION_DELETE_SECRETS,
userId: req.user._id,
workspaceId: new Types.ObjectId(workspaceId),
secretIds: deleteSecrets
secretIds: deleteSecretIds
})) as IAction;
actions.push(deleteAction);
@ -351,7 +439,7 @@ export const batchSecrets = async (req: Request, res: Response) => {
}
if (deleteSecrets.length > 0) {
resObj["deletedSecrets"] = deleteSecrets.map((d) => d.toString());
resObj["deletedSecrets"] = deleteSecrets.map((d) => d._id.toString());
}
return res.status(200).send(resObj);
@ -416,7 +504,7 @@ export const createSecrets = async (req: Request, res: Response) => {
}
*/
const channel = getChannelFromUserAgent(req.headers["user-agent"]);
const channel = getUserAgentType(req.headers["user-agent"]);
const {
workspaceId,
environment,
@ -697,10 +785,16 @@ export const getSecrets = async (req: Request, res: Response) => {
const environment = req.query.environment as string;
const folders = await Folder.findOne({ workspace: workspaceId, environment });
if ((!folders && folderId && folderId !== "root") || (!folders && secretPath)) {
if (
// if no folders and asking for a non root folder id or non root secret path
(!folders && folderId && folderId !== "root") ||
(!folders && secretPath && secretPath !== "/")
) {
res.send({ secrets: [] });
return;
}
if (folders && folderId !== "root") {
const folder = searchByFolderId(folders.nodes, folderId as string);
if (!folder) {
@ -834,7 +928,7 @@ export const getSecrets = async (req: Request, res: Response) => {
importedSecrets = await getAllImportedSecrets(workspaceId, environment, folderId as string);
}
const channel = getChannelFromUserAgent(req.headers["user-agent"]);
const channel = getUserAgentType(req.headers["user-agent"]);
const readAction = await EELogService.createAction({
name: ACTION_READ_SECRETS,
@ -855,6 +949,21 @@ export const getSecrets = async (req: Request, res: Response) => {
channel,
ipAddress: req.realIP
}));
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.GET_SECRETS,
metadata: {
environment,
secretPath: secretPath as string,
numberOfSecrets: secrets.length
}
},
{
workspaceId: new Types.ObjectId(workspaceId as string)
}
);
const postHogClient = await TelemetryService.getPostHogClient();
if (postHogClient) {
@ -1170,7 +1279,7 @@ export const deleteSecrets = async (req: Request, res: Response) => {
}
*/
const channel = getChannelFromUserAgent(req.headers["user-agent"]);
const channel = getUserAgentType(req.headers["user-agent"]);
const toDelete = req.secrets.map((s: any) => s._id);
await Secret.deleteMany({

@ -1,10 +1,11 @@
import { Request, Response } from "express";
import crypto from "crypto";
import bcrypt from "bcrypt";
import { ServiceAccount, ServiceTokenData, User } from "../../models";
import { AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT } from "../../variables";
import { ServiceTokenData } from "../../models";
import { getSaltRounds } from "../../config";
import { BadRequestError } from "../../utils/errors";
import { ActorType, EventType } from "../../ee/models";
import { EEAuditLogService } from "../../ee/services";
/**
* Return service token data associated with service token on request
@ -73,24 +74,16 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
}
let user, serviceAccount;
if (req.authData.authMode === AUTH_MODE_JWT && req.authData.authPayload instanceof User) {
let user;
if (req.authData.actor.type === ActorType.USER) {
user = req.authData.authPayload._id;
}
if (
req.authData.authMode === AUTH_MODE_SERVICE_ACCOUNT &&
req.authData.authPayload instanceof ServiceAccount
) {
serviceAccount = req.authData.authPayload._id;
}
serviceTokenData = await new ServiceTokenData({
name,
workspace: workspaceId,
user,
serviceAccount,
scopes,
lastUsed: new Date(),
expiresAt,
@ -107,6 +100,20 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
if (!serviceTokenData) throw new Error("Failed to find service token data");
const serviceToken = `st.${serviceTokenData._id.toString()}.${secret}`;
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.CREATE_SERVICE_TOKEN,
metadata: {
name,
scopes
}
},
{
workspaceId
}
);
return res.status(200).send({
serviceToken,
@ -124,6 +131,24 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => {
const { serviceTokenDataId } = req.params;
const serviceTokenData = await ServiceTokenData.findByIdAndDelete(serviceTokenDataId);
if (!serviceTokenData) return res.status(200).send({
message: "Failed to delete service token"
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.DELETE_SERVICE_TOKEN,
metadata: {
name: serviceTokenData.name,
scopes: serviceTokenData?.scopes
}
},
{
workspaceId: serviceTokenData.workspace
}
);
return res.status(200).send({
serviceTokenData

@ -4,7 +4,7 @@ import crypto from "crypto";
import bcrypt from "bcrypt";
import {
APIKeyData,
AuthProvider,
AuthMethod,
MembershipOrg,
TokenVersion,
User
@ -113,21 +113,26 @@ export const updateName = async (req: Request, res: Response) => {
}
/**
* Update auth provider of the current user to [authProvider]
* Update auth method of the current user to [authMethods]
* @param req
* @param res
* @returns
*/
export const updateAuthProvider = async (req: Request, res: Response) => {
export const updateAuthMethods = async (req: Request, res: Response) => {
const {
authProvider
authMethods
} = req.body;
if (
req.user?.authProvider === AuthProvider.OKTA_SAML
|| req.user?.authProvider === AuthProvider.AZURE_SAML
|| req.user?.authProvider === AuthProvider.JUMPCLOUD_SAML
) {
const hasSamlEnabled = req.user.authMethods
.some(
(authMethod: AuthMethod) => [
AuthMethod.OKTA_SAML,
AuthMethod.AZURE_SAML,
AuthMethod.JUMPCLOUD_SAML
].includes(authMethod)
);
if (hasSamlEnabled) {
return res.status(400).send({
message: "Failed to update user authentication method because SAML SSO is enforced"
});
@ -136,7 +141,7 @@ export const updateAuthProvider = async (req: Request, res: Response) => {
const user = await User.findByIdAndUpdate(
req.user._id.toString(),
{
authProvider
authMethods
},
{
new: true
@ -148,6 +153,7 @@ export const updateAuthProvider = async (req: Request, res: Response) => {
});
}
/**
* Return organizations that the current user is part of.
* @param req

@ -9,6 +9,8 @@ import {
import { pushKeys } from "../../helpers/key";
import { EventService, TelemetryService } from "../../services";
import { eventPushSecrets } from "../../events";
import { EEAuditLogService } from "../../ee/services";
import { EventType } from "../../ee/models";
interface V2PushSecret {
type: string; // personal or shared
@ -180,16 +182,30 @@ export const getWorkspaceKey = async (req: Request, res: Response) => {
}
*/
const { workspaceId } = req.params;
const key = await Key.findOne({
workspace: workspaceId,
receiver: req.user._id
}).populate("sender", "+publicKey");
if (!key) throw new Error("Failed to find workspace key");
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.GET_WORKSPACE_KEY,
metadata: {
keyId: key._id.toString()
}
},
{
workspaceId: new Types.ObjectId(workspaceId)
}
);
return res.status(200).json(key);
};
export const getWorkspaceServiceTokenData = async (req: Request, res: Response) => {
const { workspaceId } = req.params;

@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import { Request, Response } from "express";
import jwt from "jsonwebtoken";
import * as Sentry from "@sentry/node";
import * as bigintConversion from "bigint-conversion";
const jsrp = require("jsrp");
import { LoginSRPDetail, User } from "../../models";
@ -15,19 +14,19 @@ import {
ACTION_LOGIN,
TOKEN_EMAIL_MFA,
} from "../../variables";
import { getChannelFromUserAgent } from "../../utils/posthog"; // TODO: move this
import { getUserAgentType } from "../../utils/posthog"; // TODO: move this
import {
getHttpsEnabled,
getJwtMfaLifetime,
getJwtMfaSecret,
} from "../../config";
import { AuthProvider } from "../../models/user";
import { AuthMethod } from "../../models/user";
declare module "jsonwebtoken" {
export interface ProviderAuthJwtPayload extends jwt.JwtPayload {
userId: string;
email: string;
authProvider: AuthProvider;
authProvider: AuthMethod;
isUserCompleted: boolean,
}
}
@ -39,62 +38,53 @@ declare module "jsonwebtoken" {
* @returns
*/
export const login1 = async (req: Request, res: Response) => {
try {
const {
const {
email,
providerAuthToken,
clientPublicKey,
}: {
email: string;
clientPublicKey: string,
providerAuthToken?: string;
} = req.body;
const user = await User.findOne({
email,
}).select("+salt +verifier");
if (!user) throw new Error("Failed to find user");
if (!user.authMethods.includes(AuthMethod.EMAIL)) {
await validateProviderAuthToken({
email,
providerAuthToken,
clientPublicKey,
}: {
email: string;
clientPublicKey: string,
providerAuthToken?: string;
} = req.body;
const user = await User.findOne({
email,
}).select("+salt +verifier");
if (!user) throw new Error("Failed to find user");
});
}
if (user.authProvider && user.authProvider !== AuthProvider.EMAIL) {
await validateProviderAuthToken({
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier,
},
async () => {
// generate server-side public key
const serverPublicKey = server.getPublicKey();
await LoginSRPDetail.findOneAndReplace({
email: email,
}, {
email,
user,
providerAuthToken,
})
}
userId: user.id,
clientPublicKey: clientPublicKey,
serverBInt: bigintConversion.bigintToBuf(server.bInt),
}, { upsert: true, returnNewDocument: false });
const server = new jsrp.server();
server.init(
{
return res.status(200).send({
serverPublicKey,
salt: user.salt,
verifier: user.verifier,
},
async () => {
// generate server-side public key
const serverPublicKey = server.getPublicKey();
await LoginSRPDetail.findOneAndReplace({
email: email,
}, {
email,
userId: user.id,
clientPublicKey: clientPublicKey,
serverBInt: bigintConversion.bigintToBuf(server.bInt),
}, { upsert: true, returnNewDocument: false });
return res.status(200).send({
serverPublicKey,
salt: user.salt,
});
}
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to start authentication process",
});
}
});
}
);
};
/**
@ -105,159 +95,150 @@ export const login1 = async (req: Request, res: Response) => {
* @returns
*/
export const login2 = async (req: Request, res: Response) => {
try {
if (!req.headers["user-agent"]) throw InternalServerError({ message: "User-Agent header is required" });
const { email, clientProof, providerAuthToken } = req.body;
const user = await User.findOne({
email,
}).select("+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices");
if (!user) throw new Error("Failed to find user");
if (user.authProvider && user.authProvider !== AuthProvider.EMAIL) {
await validateProviderAuthToken({
email,
user,
providerAuthToken,
})
}
const loginSRPDetail = await LoginSRPDetail.findOneAndDelete({ email: email })
if (!loginSRPDetail) {
return BadRequestError(Error("Failed to find login details for SRP"))
}
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier,
b: loginSRPDetail.serverBInt,
},
async () => {
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
// compare server and client shared keys
if (server.checkClientProof(clientProof)) {
if (!req.headers["user-agent"]) throw InternalServerError({ message: "User-Agent header is required" });
if (user.isMfaEnabled) {
// case: user has MFA enabled
const { email, clientProof, providerAuthToken } = req.body;
// generate temporary MFA token
const token = createToken({
payload: {
userId: user._id.toString(),
},
expiresIn: await getJwtMfaLifetime(),
secret: await getJwtMfaSecret(),
});
const user = await User.findOne({
email,
}).select("+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices");
const code = await TokenService.createToken({
type: TOKEN_EMAIL_MFA,
email,
});
if (!user) throw new Error("Failed to find user");
if (!user.authMethods.includes(AuthMethod.EMAIL)) {
await validateProviderAuthToken({
email,
providerAuthToken,
})
}
// send MFA code [code] to [email]
await sendMail({
template: "emailMfa.handlebars",
subjectLine: "Infisical MFA code",
recipients: [user.email],
substitutions: {
code,
},
});
const loginSRPDetail = await LoginSRPDetail.findOneAndDelete({ email: email })
return res.status(200).send({
mfaEnabled: true,
token,
});
}
if (!loginSRPDetail) {
return BadRequestError(Error("Failed to find login details for SRP"))
}
await checkUserDevice({
user,
ip: req.realIP,
userAgent: req.headers["user-agent"] ?? "",
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier,
b: loginSRPDetail.serverBInt,
},
async () => {
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
// compare server and client shared keys
if (server.checkClientProof(clientProof)) {
if (user.isMfaEnabled) {
// case: user has MFA enabled
// generate temporary MFA token
const token = createToken({
payload: {
userId: user._id.toString(),
},
expiresIn: await getJwtMfaLifetime(),
secret: await getJwtMfaSecret(),
});
// 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(),
const code = await TokenService.createToken({
type: TOKEN_EMAIL_MFA,
email,
});
// case: user does not have MFA enablgged
// return (access) token in response
// send MFA code [code] to [email]
await sendMail({
template: "emailMfa.handlebars",
subjectLine: "Infisical MFA code",
recipients: [user.email],
substitutions: {
code,
},
});
interface ResponseData {
mfaEnabled: boolean;
encryptionVersion: any;
protectedKey?: string;
protectedKeyIV?: string;
protectedKeyTag?: string;
token: string;
publicKey?: string;
encryptedPrivateKey?: string;
iv?: string;
tag?: string;
}
return res.status(200).send({
mfaEnabled: true,
token,
});
}
const response: ResponseData = {
mfaEnabled: false,
encryptionVersion: user.encryptionVersion,
token: tokens.token,
publicKey: user.publicKey,
encryptedPrivateKey: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag,
}
await checkUserDevice({
user,
ip: req.realIP,
userAgent: req.headers["user-agent"] ?? "",
});
if (
user?.protectedKey &&
user?.protectedKeyIV &&
user?.protectedKeyTag
) {
response.protectedKey = user.protectedKey;
response.protectedKeyIV = user.protectedKeyIV
response.protectedKeyTag = user.protectedKeyTag;
}
// 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(),
});
const loginAction = await EELogService.createAction({
name: ACTION_LOGIN,
userId: user._id,
});
// 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;
}
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers["user-agent"]),
ipAddress: req.realIP,
});
const response: ResponseData = {
mfaEnabled: false,
encryptionVersion: user.encryptionVersion,
token: tokens.token,
publicKey: user.publicKey,
encryptedPrivateKey: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag,
}
return res.status(200).send(response);
if (
user?.protectedKey &&
user?.protectedKeyIV &&
user?.protectedKeyTag
) {
response.protectedKey = user.protectedKey;
response.protectedKeyIV = user.protectedKeyIV
response.protectedKeyTag = user.protectedKeyTag;
}
return res.status(400).send({
message: "Failed to authenticate. Try again?",
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);
}
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to authenticate. Try again?",
});
}
return res.status(400).send({
message: "Failed to authenticate. Try again?",
});
}
);
};

@ -362,7 +362,8 @@ export const createSecret = async (req: Request, res: Response) => {
secretCommentCiphertext,
secretCommentIV,
secretCommentTag,
secretPath = "/"
secretPath = "/",
metadata
} = req.body;
const secret = await SecretService.createSecret({
@ -380,7 +381,8 @@ export const createSecret = async (req: Request, res: Response) => {
secretPath,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
secretCommentTag,
metadata
});
await EventService.handleEvent({

@ -12,7 +12,7 @@ import { standardRequest } from "../../config/request";
import { getHttpsEnabled, getJwtSignupSecret, getLoopsApiKey } from "../../config";
import { BadRequestError } from "../../utils/errors";
import { TelemetryService } from "../../services";
import { AuthProvider } from "../../models";
import { AuthMethod } from "../../models";
/**
* Complete setting up user by adding their personal and auth information as part of the
@ -71,8 +71,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
if (providerAuthToken) {
await validateProviderAuthToken({
email,
providerAuthToken,
user,
providerAuthToken
});
} else {
const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>req.headers["authorization"]?.split(" ", 2) ?? [null, null]
@ -117,7 +116,9 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
if (!user)
throw new Error("Failed to complete account for non-existent user"); // ensure user is non-null
if (user.authProvider !== AuthProvider.OKTA_SAML) {
const hasSamlEnabled = user.authMethods.some((authMethod: AuthMethod) => [AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes(authMethod));
if (!hasSamlEnabled) { // TODO: modify this part
// initialize default organization and workspace
await initializeDefaultOrg({
organizationName,

@ -1,10 +1,12 @@
import { Request, Response } from "express";
import { Membership, Workspace } from "../../../models";
import { IUser, Membership, Workspace } from "../../../models";
import { EventType } from "../../../ee/models";
import { IMembershipPermission } from "../../../models/membership";
import { BadRequestError, UnauthorizedRequestError } from "../../../utils/errors";
import { ADMIN, MEMBER } from "../../../variables/organization";
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../../variables";
import _ from "lodash";
import { EEAuditLogService } from "../../services";
export const denyMembershipPermissions = async (req: Request, res: Response) => {
const { membershipId } = req.params;
@ -51,12 +53,33 @@ export const denyMembershipPermissions = async (req: Request, res: Response) =>
{ _id: membershipToModify._id },
{ $set: { deniedPermissions: sanitizedMembershipPermissionsUnique } },
{ new: true }
)
).populate<{ user: IUser }>("user");
if (!updatedMembershipWithPermissions) {
throw BadRequestError({ message: "The resource has been removed before it can be modified" })
}
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS,
metadata: {
userId: updatedMembershipWithPermissions.user._id.toString(),
email: updatedMembershipWithPermissions.user.email,
deniedPermissions: updatedMembershipWithPermissions.deniedPermissions.map(({
environmentSlug,
ability
}) => ({
environmentSlug,
ability
}))
}
},
{
workspaceId: updatedMembershipWithPermissions.workspace
}
);
res.send({
permissionsDenied: updatedMembershipWithPermissions.deniedPermissions,
})

@ -1,3 +1,4 @@
import { Types } from "mongoose";
import { Request, Response } from "express";
import { getLicenseServerUrl } from "../../../config";
import { licenseServerKeyRequest } from "../../../config/request";
@ -20,7 +21,7 @@ export const getOrganizationPlan = async (req: Request, res: Response) => {
const { organizationId } = req.params;
const workspaceId = req.query.workspaceId as string;
const plan = await EELicenseService.getPlan(organizationId, workspaceId);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId), new Types.ObjectId(workspaceId));
return res.status(200).send({
plan,
@ -44,7 +45,7 @@ export const startOrganizationTrial = async (req: Request, res: Response) => {
}
);
EELicenseService.delPlan(organizationId);
EELicenseService.delPlan(new Types.ObjectId(organizationId));
return res.status(200).send({
url

@ -3,6 +3,7 @@ import { Types } from "mongoose";
import { BotOrgService } from "../../../services";
import { SSOConfig } from "../../models";
import {
AuthMethod,
MembershipOrg,
User
} from "../../../models";
@ -59,7 +60,7 @@ export const updateSSOConfig = async (req: Request, res: Response) => {
cert,
} = req.body;
const plan = await EELicenseService.getPlan(organizationId);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId));
if (!plan.samlSSO) return res.status(400).send({
message: "Failed to update SAML SSO configuration due to plan restriction. Upgrade plan to update SSO configuration."
@ -156,7 +157,7 @@ export const updateSSOConfig = async (req: Request, res: Response) => {
}
},
{
authProvider: ssoConfig.authProvider
authMethods: [ssoConfig.authProvider],
}
);
} else {
@ -167,9 +168,7 @@ export const updateSSOConfig = async (req: Request, res: Response) => {
}
},
{
$unset: {
authProvider: 1
}
authMethods: [AuthMethod.EMAIL],
}
);
}
@ -194,7 +193,7 @@ export const createSSOConfig = async (req: Request, res: Response) => {
cert
} = req.body;
const plan = await EELicenseService.getPlan(organizationId);
const plan = await EELicenseService.getPlan(new Types.ObjectId(organizationId));
if (!plan.samlSSO) return res.status(400).send({
message: "Failed to create SAML SSO configuration due to plan restriction. Upgrade plan to add SSO configuration."

@ -8,6 +8,6 @@ import { Request, Response } from "express";
*/
export const getMyIp = (req: Request, res: Response) => {
return res.status(200).send({
ip: req.authData.authIP
ip: req.authData.ipAddress
});
}

@ -1,21 +1,26 @@
import { Request, Response } from "express";
import { PipelineStage, Types } from "mongoose";
import { Secret } from "../../../models";
import { Membership, Secret, ServiceTokenData, User } from "../../../models";
import {
ActorType,
AuditLog,
EventType,
FolderVersion,
IPType,
ISecretVersion,
Log,
SecretSnapshot,
SecretVersion,
ServiceActor,
TFolderRootVersionSchema,
TrustedIP
TrustedIP,
UserActor
} from "../../models";
import { EESecretService } from "../../services";
import { getLatestSecretVersionIds } from "../../helpers/secretVersion";
import Folder, { TFolderSchema } from "../../../models/folder";
import { searchByFolderId } from "../../../services/FolderService";
import { EELicenseService } from "../../services";
import { EEAuditLogService, EELicenseService } from "../../services";
import { extractIPDetails, isValidIpOrCidr } from "../../../utils/ip";
/**
@ -593,6 +598,101 @@ export const getWorkspaceLogs = async (req: Request, res: Response) => {
});
};
/**
* Return audit logs for workspace with id [workspaceId]
* @param req
* @param res
*/
export const getWorkspaceAuditLogs = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
const eventType = req.query.eventType;
const userAgentType = req.query.userAgentType;
const actor = req.query.actor as string | undefined;
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 endDate = req.query.endDate as string;
const query = {
workspace: new Types.ObjectId(workspaceId),
...(eventType ? {
"event.type": eventType
} : {}),
...(userAgentType ? {
userAgentType
} : {}),
...(actor ? {
"actor.type": actor.split("-", 2)[0],
...(actor.split("-", 2)[0] === ActorType.USER ? {
"actor.metadata.userId": actor.split("-", 2)[1]
} : {
"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)
.sort({ createdAt: -1 })
.skip(offset)
.limit(limit);
const totalCount = await AuditLog.countDocuments(query);
return res.status(200).send({
auditLogs,
totalCount
});
}
/**
* Return audit log actor filter options for workspace with id [workspaceId]
* @param req
* @param res
*/
export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
const userIds = await Membership.distinct("user", {
workspace: new Types.ObjectId(workspaceId)
});
const userActors: UserActor[] = (await User.find({
_id: {
$in: userIds
}
})
.select("email"))
.map((user) => ({
type: ActorType.USER,
metadata: {
userId: user._id.toString(),
email: user.email
}
}));
const serviceActors: ServiceActor[] = (await ServiceTokenData.find({
workspace: new Types.ObjectId(workspaceId)
})
.select("name"))
.map((serviceTokenData) => ({
type: ActorType.SERVICE,
metadata: {
serviceId: serviceTokenData._id.toString(),
name: serviceTokenData.name
}
}));
return res.status(200).send({
actors: [...userActors, ...serviceActors]
});
}
/**
* Return trusted ips for workspace with id [workspaceId]
* @param req
@ -623,7 +723,7 @@ export const addWorkspaceTrustedIp = async (req: Request, res: Response) => {
isActive
} = req.body;
const plan = await EELicenseService.getPlan(req.workspace.organization.toString());
const plan = await EELicenseService.getPlan(req.workspace.organization);
if (!plan.ipAllowlisting) return res.status(400).send({
message: "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range."
@ -645,6 +745,21 @@ export const addWorkspaceTrustedIp = async (req: Request, res: Response) => {
isActive,
comment,
}).save();
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.ADD_TRUSTED_IP,
metadata: {
trustedIpId: trustedIp._id.toString(),
ipAddress: trustedIp.ipAddress,
prefix: trustedIp.prefix
}
},
{
workspaceId: trustedIp.workspace
}
);
return res.status(200).send({
trustedIp
@ -663,7 +778,7 @@ export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => {
comment
} = req.body;
const plan = await EELicenseService.getPlan(req.workspace.organization.toString());
const plan = await EELicenseService.getPlan(req.workspace.organization);
if (!plan.ipAllowlisting) return res.status(400).send({
message: "Failed to update IP access range due to plan restriction. Upgrade plan to update IP access range."
@ -708,6 +823,25 @@ export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => {
}
);
if (!trustedIp) return res.status(400).send({
message: "Failed to update trusted IP"
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.UPDATE_TRUSTED_IP,
metadata: {
trustedIpId: trustedIp._id.toString(),
ipAddress: trustedIp.ipAddress,
prefix: trustedIp.prefix
}
},
{
workspaceId: trustedIp.workspace
}
);
return res.status(200).send({
trustedIp
});
@ -721,7 +855,7 @@ export const updateWorkspaceTrustedIp = async (req: Request, res: Response) => {
export const deleteWorkspaceTrustedIp = async (req: Request, res: Response) => {
const { workspaceId, trustedIpId } = req.params;
const plan = await EELicenseService.getPlan(req.workspace.organization.toString());
const plan = await EELicenseService.getPlan(req.workspace.organization);
if (!plan.ipAllowlisting) return res.status(400).send({
message: "Failed to delete IP access range due to plan restriction. Upgrade plan to delete IP access range."
@ -731,6 +865,25 @@ export const deleteWorkspaceTrustedIp = async (req: Request, res: Response) => {
_id: new Types.ObjectId(trustedIpId),
workspace: new Types.ObjectId(workspaceId)
});
if (!trustedIp) return res.status(400).send({
message: "Failed to delete trusted IP"
});
await EEAuditLogService.createAuditLog(
req.authData,
{
type: EventType.DELETE_TRUSTED_IP,
metadata: {
trustedIpId: trustedIp._id.toString(),
ipAddress: trustedIp.ipAddress,
prefix: trustedIp.prefix
}
},
{
workspaceId: trustedIp.workspace
}
);
return res.status(200).send({
trustedIp

@ -0,0 +1,76 @@
import { Schema, Types, model } from "mongoose";
import {
ActorType,
EventType,
UserAgentType
} from "./enums";
import {
Actor,
Event
} from "./types";
export interface IAuditLog {
actor: Actor;
organization: Types.ObjectId;
workspace: Types.ObjectId;
ipAddress: string;
event: Event;
userAgent: string;
userAgentType: UserAgentType;
expiresAt: Date;
}
const auditLogSchema = new Schema<IAuditLog>(
{
actor: {
type: {
type: String,
enum: ActorType,
required: true
},
metadata: {
type: Schema.Types.Mixed
}
},
organization: {
type: Schema.Types.ObjectId,
required: false
},
workspace: {
type: Schema.Types.ObjectId,
required: false
},
ipAddress: {
type: String,
required: true
},
event: {
type: {
type: String,
enum: EventType,
required: true
},
metadata: {
type: Schema.Types.Mixed
}
},
userAgent: {
type: String,
required: true
},
userAgentType: {
type: String,
enum: UserAgentType,
required: true
},
expiresAt: {
type: Date,
expires: 0
}
},
{
timestamps: true
}
);
export const AuditLog = model<IAuditLog>("AuditLog", auditLogSchema);

@ -0,0 +1,47 @@
export enum ActorType {
USER = "user",
SERVICE = "service"
}
export enum UserAgentType {
WEB = "web",
CLI = "cli",
K8_OPERATOR = "k8-operator",
OTHER = "other"
}
export enum EventType {
GET_SECRETS = "get-secrets",
GET_SECRET = "get-secret",
REVEAL_SECRET = "reveal-secret",
CREATE_SECRET = "create-secret",
UPDATE_SECRET = "update-secret",
DELETE_SECRET = "delete-secret",
GET_WORKSPACE_KEY = "get-workspace-key",
AUTHORIZE_INTEGRATION = "authorize-integration",
UNAUTHORIZE_INTEGRATION = "unauthorize-integration",
CREATE_INTEGRATION = "create-integration",
DELETE_INTEGRATION = "delete-integration",
ADD_TRUSTED_IP = "add-trusted-ip",
UPDATE_TRUSTED_IP = "update-trusted-ip",
DELETE_TRUSTED_IP = "delete-trusted-ip",
CREATE_SERVICE_TOKEN = "create-service-token",
DELETE_SERVICE_TOKEN = "delete-service-token",
CREATE_ENVIRONMENT = "create-environment",
UPDATE_ENVIRONMENT = "update-environment",
DELETE_ENVIRONMENT = "delete-environment",
ADD_WORKSPACE_MEMBER = "add-workspace-member",
REMOVE_WORKSPACE_MEMBER = "remove-workspace-member",
CREATE_FOLDER = "create-folder",
UPDATE_FOLDER = "update-folder",
DELETE_FOLDER = "delete-folder",
CREATE_WEBHOOK = "create-webhook",
UPDATE_WEBHOOK_STATUS = "update-webhook-status",
DELETE_WEBHOOK = "delete-webhook",
GET_SECRET_IMPORTS = "get-secret-imports",
CREATE_SECRET_IMPORT = "create-secret-import",
UPDATE_SECRET_IMPORT = "update-secret-import",
DELETE_SECRET_IMPORT = "delete-secret-import",
UPDATE_USER_WORKSPACE_ROLE = "update-user-workspace-role",
UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS = "update-user-workspace-denied-permissions"
}

@ -0,0 +1,3 @@
export * from "./auditLog";
export * from "./enums";
export * from "./types";

@ -0,0 +1,403 @@
import {
ActorType,
EventType
} from "./enums";
interface UserActorMetadata {
userId: string;
email: string;
}
interface ServiceActorMetadata {
serviceId: string;
name: string;
}
export interface UserActor {
type: ActorType.USER;
metadata: UserActorMetadata;
}
export interface ServiceActor {
type: ActorType.SERVICE;
metadata: ServiceActorMetadata;
}
export type Actor =
| UserActor
| ServiceActor;
interface GetSecretsEvent {
type: EventType.GET_SECRETS;
metadata: {
environment: string;
secretPath: string;
numberOfSecrets: number;
};
}
interface GetSecretEvent {
type: EventType.GET_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
};
}
interface CreateSecretEvent {
type: EventType.CREATE_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
}
}
interface UpdateSecretEvent {
type: EventType.UPDATE_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
}
}
interface DeleteSecretEvent {
type: EventType.DELETE_SECRET;
metadata: {
environment: string;
secretPath: string;
secretId: string;
secretKey: string;
secretVersion: number;
}
}
interface GetWorkspaceKeyEvent {
type: EventType.GET_WORKSPACE_KEY,
metadata: {
keyId: string;
}
}
interface AuthorizeIntegrationEvent {
type: EventType.AUTHORIZE_INTEGRATION;
metadata: {
integration: string;
}
}
interface UnauthorizeIntegrationEvent {
type: EventType.UNAUTHORIZE_INTEGRATION;
metadata: {
integration: string;
}
}
interface CreateIntegrationEvent {
type: EventType.CREATE_INTEGRATION;
metadata: {
integrationId: string;
integration: string; // TODO: fix type
environment: string;
secretPath: string;
url?: string;
app?: string;
appId?: string;
targetEnvironment?: string;
targetEnvironmentId?: string;
targetService?: string;
targetServiceId?: string;
path?: string;
region?: string;
}
}
interface DeleteIntegrationEvent {
type: EventType.DELETE_INTEGRATION;
metadata: {
integrationId: string;
integration: string; // TODO: fix type
environment: string;
secretPath: string;
url?: string;
app?: string;
appId?: string;
targetEnvironment?: string;
targetEnvironmentId?: string;
targetService?: string;
targetServiceId?: string;
path?: string;
region?: string;
}
}
interface AddTrustedIPEvent {
type: EventType.ADD_TRUSTED_IP;
metadata: {
trustedIpId: string;
ipAddress: string;
prefix?: number;
}
}
interface UpdateTrustedIPEvent {
type: EventType.UPDATE_TRUSTED_IP;
metadata: {
trustedIpId: string;
ipAddress: string;
prefix?: number;
}
}
interface DeleteTrustedIPEvent {
type: EventType.DELETE_TRUSTED_IP;
metadata: {
trustedIpId: string;
ipAddress: string;
prefix?: number;
}
}
interface CreateServiceTokenEvent {
type: EventType.CREATE_SERVICE_TOKEN;
metadata: {
name: string;
scopes: Array<{
environment: string;
secretPath: string;
}>;
}
}
interface DeleteServiceTokenEvent {
type: EventType.DELETE_SERVICE_TOKEN;
metadata: {
name: string;
scopes: Array<{
environment: string;
secretPath: string;
}>;
}
}
interface CreateEnvironmentEvent {
type: EventType.CREATE_ENVIRONMENT;
metadata: {
name: string;
slug: string;
}
}
interface UpdateEnvironmentEvent {
type: EventType.UPDATE_ENVIRONMENT;
metadata: {
oldName: string;
newName: string;
oldSlug: string;
newSlug: string;
}
}
interface DeleteEnvironmentEvent {
type: EventType.DELETE_ENVIRONMENT;
metadata: {
name: string;
slug: string;
}
}
interface AddWorkspaceMemberEvent {
type: EventType.ADD_WORKSPACE_MEMBER;
metadata: {
userId: string;
email: string;
}
}
interface RemoveWorkspaceMemberEvent {
type: EventType.REMOVE_WORKSPACE_MEMBER;
metadata: {
userId: string;
email: string;
}
}
interface CreateFolderEvent {
type: EventType.CREATE_FOLDER;
metadata: {
environment: string;
folderId: string;
folderName: string;
folderPath: string;
}
}
interface UpdateFolderEvent {
type: EventType.UPDATE_FOLDER;
metadata: {
environment: string;
folderId: string;
oldFolderName: string;
newFolderName: string;
folderPath: string;
}
}
interface DeleteFolderEvent {
type: EventType.DELETE_FOLDER;
metadata: {
environment: string;
folderId: string;
folderName: string;
folderPath: string;
}
}
interface CreateWebhookEvent {
type: EventType.CREATE_WEBHOOK,
metadata: {
webhookId: string;
environment: string;
secretPath: string;
webhookUrl: string;
isDisabled: boolean;
}
}
interface UpdateWebhookStatusEvent {
type: EventType.UPDATE_WEBHOOK_STATUS,
metadata: {
webhookId: string;
environment: string;
secretPath: string;
webhookUrl: string;
isDisabled: boolean;
}
}
interface DeleteWebhookEvent {
type: EventType.DELETE_WEBHOOK,
metadata: {
webhookId: string;
environment: string;
secretPath: string;
webhookUrl: string;
isDisabled: boolean;
}
}
interface GetSecretImportsEvent {
type: EventType.GET_SECRET_IMPORTS,
metadata: {
environment: string;
secretImportId: string;
folderId: string;
numberOfImports: number;
}
}
interface CreateSecretImportEvent {
type: EventType.CREATE_SECRET_IMPORT,
metadata: {
secretImportId: string;
folderId: string;
importFromEnvironment: string;
importFromSecretPath: string;
importToEnvironment: string;
importToSecretPath: string;
}
}
interface UpdateSecretImportEvent {
type: EventType.UPDATE_SECRET_IMPORT,
metadata: {
secretImportId: string;
folderId: string;
importToEnvironment: string;
importToSecretPath: string;
orderBefore: {
environment: string;
secretPath: string;
}[],
orderAfter: {
environment: string;
secretPath: string;
}[]
}
}
interface DeleteSecretImportEvent {
type: EventType.DELETE_SECRET_IMPORT,
metadata: {
secretImportId: string;
folderId: string;
importFromEnvironment: string;
importFromSecretPath: string;
importToEnvironment: string;
importToSecretPath: string;
}
}
interface UpdateUserRole {
type: EventType.UPDATE_USER_WORKSPACE_ROLE,
metadata: {
userId: string;
email: string;
oldRole: string;
newRole: string;
}
}
interface UpdateUserDeniedPermissions {
type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS,
metadata: {
userId: string;
email: string;
deniedPermissions: {
environmentSlug: string;
ability: string;
}[]
}
}
export type Event =
| GetSecretsEvent
| GetSecretEvent
| CreateSecretEvent
| UpdateSecretEvent
| DeleteSecretEvent
| GetWorkspaceKeyEvent
| AuthorizeIntegrationEvent
| UnauthorizeIntegrationEvent
| CreateIntegrationEvent
| DeleteIntegrationEvent
| AddTrustedIPEvent
| UpdateTrustedIPEvent
| DeleteTrustedIPEvent
| CreateServiceTokenEvent
| DeleteServiceTokenEvent
| CreateEnvironmentEvent
| UpdateEnvironmentEvent
| DeleteEnvironmentEvent
| AddWorkspaceMemberEvent
| RemoveWorkspaceMemberEvent
| CreateFolderEvent
| UpdateFolderEvent
| DeleteFolderEvent
| CreateWebhookEvent
| UpdateWebhookStatusEvent
| DeleteWebhookEvent
| GetSecretImportsEvent
| CreateSecretImportEvent
| UpdateSecretImportEvent
| DeleteSecretImportEvent
| UpdateUserRole
| UpdateUserDeniedPermissions;

@ -5,6 +5,7 @@ export * from "./log";
export * from "./action";
export * from "./ssoConfig";
export * from "./trustedIp";
export * from "./auditLog";
export * from "./gitRisks";
export * from "./gitAppOrganizationInstallation";
export * from "./gitAppInstallationSession";

@ -6,11 +6,12 @@ import {
} from "../../../middleware";
import { query } from "express-validator";
import { cloudProductsController } from "../../controllers/v1";
import { AuthMode } from "../../../variables";
router.get(
"/",
requireAuth({
acceptedAuthModes: ["jwt", "apiKey"],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
query("billing-cycle").exists().isIn(["monthly", "yearly"]),
validateRequest,

@ -8,13 +8,13 @@ import {
import { body, param, query } from "express-validator";
import { organizationsController } from "../../controllers/v1";
import {
ACCEPTED, ADMIN, MEMBER, OWNER,
ACCEPTED, ADMIN, AuthMode, MEMBER, OWNER
} from "../../../variables";
router.get(
"/:organizationId/plans/table",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -29,7 +29,7 @@ router.get(
router.get(
"/:organizationId/plan",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -44,7 +44,7 @@ router.get(
router.post(
"/:organizationId/session/trial",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -59,7 +59,7 @@ router.post(
router.get(
"/:organizationId/plan/billing",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -74,7 +74,7 @@ router.get(
router.get(
"/:organizationId/plan/table",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -89,7 +89,7 @@ router.get(
router.get(
"/:organizationId/billing-details",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -103,7 +103,7 @@ router.get(
router.patch(
"/:organizationId/billing-details",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -119,7 +119,7 @@ router.patch(
router.get(
"/:organizationId/billing-details/payment-methods",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -133,7 +133,7 @@ router.get(
router.post(
"/:organizationId/billing-details/payment-methods",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -149,7 +149,7 @@ router.post(
router.delete(
"/:organizationId/billing-details/payment-methods/:pmtMethodId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -164,7 +164,7 @@ router.delete(
router.get(
"/:organizationId/billing-details/tax-ids",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -178,7 +178,7 @@ router.get(
router.post(
"/:organizationId/billing-details/tax-ids",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -194,7 +194,7 @@ router.post(
router.delete(
"/:organizationId/billing-details/tax-ids/:taxId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -209,7 +209,7 @@ router.delete(
router.get(
"/:organizationId/invoices",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -223,7 +223,7 @@ router.get(
router.get(
"/:organizationId/licenses",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],

@ -9,15 +9,16 @@ import { body, param, query } from "express-validator";
import { secretController } from "../../controllers/v1";
import {
ADMIN,
AuthMode,
MEMBER,
PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS,
PERMISSION_WRITE_SECRETS
} from "../../../variables";
router.get(
"/:secretId/secret-versions",
requireAuth({
acceptedAuthModes: ["jwt", "apiKey"],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireSecretAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -33,7 +34,7 @@ router.get(
router.post(
"/:secretId/secret-versions/rollback",
requireAuth({
acceptedAuthModes: ["jwt", "apiKey"],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireSecretAuth({
acceptedRoles: [ADMIN, MEMBER],

@ -7,12 +7,12 @@ import {
} from "../../../middleware";
import { body, param } from "express-validator";
import { createInstallationSession, getCurrentOrganizationInstallationStatus, getRisksForOrganization, linkInstallationToOrganization, updateRisksStatus } from "../../../controllers/v1/secretScanningController";
import { ACCEPTED, ADMIN, MEMBER, OWNER } from "../../../variables";
import { ACCEPTED, ADMIN, AuthMode, MEMBER, OWNER } from "../../../variables";
router.post(
"/create-installation-session/organization/:organizationId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
param("organizationId").exists().trim(),
requireOrganizationAuth({
@ -26,7 +26,7 @@ router.post(
router.post(
"/link-installation",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
body("installationId").exists().trim(),
body("sessionId").exists().trim(),
@ -37,7 +37,7 @@ router.post(
router.get(
"/installation-status/organization/:organizationId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
param("organizationId").exists().trim(),
requireOrganizationAuth({
@ -51,7 +51,7 @@ router.get(
router.get(
"/organization/:organizationId/risks",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
param("organizationId").exists().trim(),
requireOrganizationAuth({
@ -65,7 +65,7 @@ router.get(
router.post(
"/organization/:organizationId/risks/:riskId/status",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
param("organizationId").exists().trim(),
param("riskId").exists().trim(),

@ -8,13 +8,13 @@ import {
validateRequest,
} from "../../../middleware";
import { param } from "express-validator";
import { ADMIN, MEMBER } from "../../../variables";
import { ADMIN, AuthMode, MEMBER } from "../../../variables";
import { secretSnapshotController } from "../../controllers/v1";
router.get(
"/:secretSnapshotId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireSecretSnapshotAuth({
acceptedRoles: [ADMIN, MEMBER],

@ -15,6 +15,7 @@ import { authLimiter } from "../../../helpers/rateLimiter";
import {
ACCEPTED,
ADMIN,
AuthMode,
OWNER
} from "../../../variables";
@ -90,7 +91,7 @@ router.post("/saml2/:ssoIdentifier",
router.get(
"/config",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
@ -105,7 +106,7 @@ router.get(
router.post(
"/config",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
@ -125,7 +126,7 @@ router.post(
router.patch(
"/config",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],

@ -3,13 +3,13 @@ const router = express.Router();
import {
requireAuth
} from "../../../middleware";
import { AUTH_MODE_API_KEY, AUTH_MODE_JWT } from "../../../variables";
import { AuthMode } from "../../../variables";
import { usersController } from "../../controllers/v1";
router.get(
"/me/ip",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
usersController.getMyIp
);

@ -8,16 +8,16 @@ import {
import { body, param, query } from "express-validator";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AuthMode,
MEMBER
} from "../../../variables";
import { workspaceController } from "../../controllers/v1";
import { EventType, UserAgentType } from "../../models";
router.get(
"/:workspaceId/secret-snapshots",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -35,7 +35,7 @@ router.get(
router.get(
"/:workspaceId/secret-snapshots/count",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -51,7 +51,7 @@ router.get(
router.post(
"/:workspaceId/secret-snapshots/rollback",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -68,7 +68,7 @@ router.post(
router.get(
"/:workspaceId/logs",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -84,11 +84,46 @@ router.get(
workspaceController.getWorkspaceLogs
);
router.get(
"/:workspaceId/audit-logs",
requireAuth({
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
);
router.get(
"/:workspaceId/audit-logs/filters/actors",
requireAuth({
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: "params",
}),
param("workspaceId").exists().trim(),
validateRequest,
workspaceController.getWorkspaceAuditLogActorFilterOpts
);
router.get(
"/:workspaceId/trusted-ips",
param("workspaceId").exists().isString().trim(),
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -105,7 +140,7 @@ router.post(
body("isActive").exists().isBoolean(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@ -122,7 +157,7 @@ router.patch(
body("comment").default("").isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@ -137,7 +172,7 @@ router.delete(
param("trustedIpId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],

@ -0,0 +1,50 @@
import { Types } from "mongoose";
import { AuditLog, Event } from "../models";
import { AuthData } from "../../interfaces/middleware";
import EELicenseService from "./EELicenseService";
import { Workspace } from "../../models";
import { OrganizationNotFoundError } from "../../utils/errors";
interface EventScope {
workspaceId?: Types.ObjectId;
organizationId?: Types.ObjectId;
}
type ValidEventScope =
| Required<Pick<EventScope, "workspaceId">>
| Required<Pick<EventScope, "organizationId">>
| Required<EventScope>
export default class EEAuditLogService {
static async createAuditLog(authData: AuthData, event: Event, eventScope: ValidEventScope, shouldSave = true) {
const MS_IN_DAY = 24 * 60 * 60 * 1000;
const organizationId = ("organizationId" in eventScope)
? eventScope.organizationId
: (await Workspace.findById(eventScope.workspaceId).select("organization").lean())?.organization;
if (!organizationId) throw OrganizationNotFoundError({
message: "createAuditLog: Failed to create audit log due to missing organizationId"
});
const ttl = (await EELicenseService.getPlan(organizationId)).auditLogsRetentionDays * MS_IN_DAY;
const auditLog = await new AuditLog({
actor: authData.actor,
organization: organizationId,
workspace: ("workspaceId" in eventScope) ? eventScope.workspaceId : undefined,
ipAddress: authData.ipAddress,
event,
userAgent: authData.userAgent,
userAgentType: authData.userAgentType,
expiresAt: new Date(Date.now() + ttl)
});
if (shouldSave) {
await auditLog.save();
}
return auditLog;
}
}

@ -1,3 +1,4 @@
import { Types } from "mongoose";
import * as Sentry from "@sentry/node";
import NodeCache from "node-cache";
import {
@ -31,6 +32,7 @@ interface FeatureSet {
customRateLimits: boolean;
customAlerts: boolean;
auditLogs: boolean;
auditLogsRetentionDays: number;
samlSSO: boolean;
status: "incomplete" | "incomplete_expired" | "trialing" | "active" | "past_due" | "canceled" | "unpaid" | null;
trial_end: number | null;
@ -63,9 +65,10 @@ class EELicenseService {
pitRecovery: false,
ipAllowlisting: false,
rbac: true,
customRateLimits: true,
customAlerts: true,
customRateLimits: false,
customAlerts: false,
auditLogs: false,
auditLogsRetentionDays: 0,
samlSSO: false,
status: null,
trial_end: null,
@ -81,10 +84,10 @@ class EELicenseService {
});
}
public async getPlan(organizationId: string, workspaceId?: string): Promise<FeatureSet> {
public async getPlan(organizationId: Types.ObjectId, workspaceId?: Types.ObjectId): Promise<FeatureSet> {
try {
if (this.instanceType === "cloud") {
const cachedPlan = this.localFeatureSet.get<FeatureSet>(`${organizationId}-${workspaceId ?? ""}`);
const cachedPlan = this.localFeatureSet.get<FeatureSet>(`${organizationId.toString()}-${workspaceId?.toString() ?? ""}`);
if (cachedPlan) {
return cachedPlan;
}
@ -101,7 +104,7 @@ class EELicenseService {
const { data: { currentPlan } } = await licenseServerKeyRequest.get(url);
// cache fetched plan for organization
this.localFeatureSet.set(`${organizationId}-${workspaceId ?? ""}`, currentPlan);
this.localFeatureSet.set(`${organizationId.toString()}-${workspaceId?.toString() ?? ""}`, currentPlan);
return currentPlan;
}
@ -112,16 +115,16 @@ class EELicenseService {
return this.globalFeatureSet;
}
public async refreshPlan(organizationId: string, workspaceId?: string) {
public async refreshPlan(organizationId: Types.ObjectId, workspaceId?: Types.ObjectId) {
if (this.instanceType === "cloud") {
this.localFeatureSet.del(`${organizationId}-${workspaceId ?? ""}`);
this.localFeatureSet.del(`${organizationId.toString()}-${workspaceId?.toString() ?? ""}`);
await this.getPlan(organizationId, workspaceId);
}
}
public async delPlan(organizationId: string) {
public async delPlan(organizationId: Types.ObjectId) {
if (this.instanceType === "cloud") {
this.localFeatureSet.del(`${organizationId}-`);
this.localFeatureSet.del(`${organizationId.toString()}-`);
}
}

@ -10,7 +10,7 @@ import EELicenseService from "./EELicenseService";
/**
* Class to handle Enterprise Edition secret actions
*/
class EESecretService {
export default class EESecretService {
/**
* Save a secret snapshot that is a copy of the current state of secrets in workspace with id
* [workspaceId] under a new snapshot with incremented version under the
@ -71,5 +71,3 @@ class EESecretService {
});
}
}
export default EESecretService;

@ -1,11 +1,13 @@
import EELicenseService from "./EELicenseService";
import EESecretService from "./EESecretService";
import EELogService from "./EELogService";
import EEAuditLogService from "./EEAuditLogService";
import GithubSecretScanningService from "./GithubSecretScanning/GithubSecretScanningService"
export {
EELicenseService,
EESecretService,
EELogService,
EEAuditLogService,
GithubSecretScanningService
}

@ -1,3 +1,4 @@
import { Request } from "express";
import { Types } from "mongoose";
import jwt from "jsonwebtoken";
import bcrypt from "bcrypt";
@ -5,7 +6,6 @@ import {
APIKeyData,
ITokenVersion,
IUser,
ServiceAccount,
ServiceTokenData,
TokenVersion,
User,
@ -14,7 +14,6 @@ import {
APIKeyDataNotFoundError,
AccountNotFoundError,
BadRequestError,
ServiceAccountNotFoundError,
ServiceTokenDataNotFoundError,
UnauthorizedRequestError,
} from "../utils/errors";
@ -26,11 +25,15 @@ import {
getJwtRefreshSecret,
} from "../config";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
AuthMode
} from "../variables";
import {
ServiceTokenAuthData,
UserAuthData
} from "../interfaces/middleware";
import { ActorType } from "../ee/models";
import { getUserAgentType } from "../utils/posthog";
/**
*
@ -42,7 +45,7 @@ export const validateAuthMode = ({
acceptedAuthModes,
}: {
headers: { [key: string]: string | string[] | undefined },
acceptedAuthModes: string[]
acceptedAuthModes: AuthMode[]
}) => {
const apiKey = headers["x-api-key"];
const authHeader = headers["authorization"];
@ -55,7 +58,7 @@ export const validateAuthMode = ({
if (typeof apiKey === "string") {
// case: treat request authentication type as via X-API-KEY (i.e. API Key)
authMode = AUTH_MODE_API_KEY;
authMode = AuthMode.API_KEY;
authTokenValue = apiKey;
}
@ -71,13 +74,10 @@ export const validateAuthMode = ({
switch (tokenValue.split(".", 1)[0]) {
case "st":
authMode = AUTH_MODE_SERVICE_TOKEN;
break;
case "sa":
authMode = AUTH_MODE_SERVICE_ACCOUNT;
authMode = AuthMode.SERVICE_TOKEN;
break;
default:
authMode = AUTH_MODE_JWT;
authMode = AuthMode.JWT;
}
authTokenValue = tokenValue;
@ -100,10 +100,12 @@ export const validateAuthMode = ({
* @returns {User} user - user corresponding to JWT token
*/
export const getAuthUserPayload = async ({
req,
authTokenValue,
}: {
req: Request,
authTokenValue: string;
}) => {
}): Promise<UserAuthData> => {
const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(authTokenValue, await getJwtAuthSecret())
);
@ -130,11 +132,25 @@ export const getAuthUserPayload = async ({
if (decodedToken.accessVersion !== tokenVersion.accessVersion) throw UnauthorizedRequestError({
message: "Failed to validate access token",
});
return ({
user,
tokenVersionId: tokenVersion._id,
});
return {
actor: {
type: ActorType.USER,
metadata: {
userId: user._id.toString(),
email: user.email
}
},
authPayload: user,
ipAddress: req.realIP,
userAgent: req.headers["user-agent"] ?? "",
userAgentType: getUserAgentType(req.headers["user-agent"])
}
// return ({
// user,
// tokenVersionId: tokenVersion._id, // what to do with this? // move this out
// });
}
/**
@ -144,10 +160,12 @@ export const getAuthUserPayload = async ({
* @returns {ServiceTokenData} serviceTokenData - service token data
*/
export const getAuthSTDPayload = async ({
req,
authTokenValue,
}: {
req: Request,
authTokenValue: string;
}) => {
}): Promise<ServiceTokenAuthData> => {
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
const serviceTokenData = await ServiceTokenData
@ -180,36 +198,21 @@ export const getAuthSTDPayload = async ({
if (!serviceTokenDataToReturn) throw ServiceTokenDataNotFoundError({ message: "Failed to find service token data" });
return serviceTokenDataToReturn;
}
/**
* Return service account access key payload
* @param {Object} obj
* @param {String} obj.authTokenValue - service account access token value
* @returns {ServiceAccount} serviceAccount
*/
export const getAuthSAAKPayload = async ({
authTokenValue,
}: {
authTokenValue: string;
}) => {
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
const serviceAccount = await ServiceAccount.findById(
Buffer.from(TOKEN_IDENTIFIER, "base64").toString("hex")
).select("+secretHash");
if (!serviceAccount) {
throw ServiceAccountNotFoundError({ message: "Failed to find service account" });
return {
actor: {
type: ActorType.SERVICE,
metadata: {
serviceId: serviceTokenDataToReturn._id.toString(),
name: serviceTokenDataToReturn.name
}
},
authPayload: serviceTokenDataToReturn,
ipAddress: req.realIP,
userAgent: req.headers["user-agent"] ?? "",
userAgentType: getUserAgentType(req.headers["user-agent"])
}
const result = await bcrypt.compare(TOKEN_SECRET, serviceAccount.secretHash);
if (!result) throw UnauthorizedRequestError({
message: "Failed to authenticate service account access key",
});
return serviceAccount;
// return serviceTokenDataToReturn;
}
/**
@ -219,10 +222,12 @@ export const getAuthSAAKPayload = async ({
* @returns {APIKeyData} apiKeyData - API key data
*/
export const getAuthAPIKeyPayload = async ({
req,
authTokenValue,
}: {
req: Request,
authTokenValue: string;
}) => {
}): Promise<UserAuthData> => {
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
let apiKeyData = await APIKeyData
@ -264,7 +269,19 @@ export const getAuthAPIKeyPayload = async ({
});
}
return user;
return {
actor: {
type: ActorType.USER,
metadata: {
userId: user._id.toString(),
email: user.email
}
},
authPayload: user,
ipAddress: req.realIP,
userAgent: req.headers["user-agent"] ?? "",
userAgentType: getUserAgentType(req.headers["user-agent"])
}
}
/**
@ -375,11 +392,9 @@ export const createToken = ({
export const validateProviderAuthToken = async ({
email,
user,
providerAuthToken,
}: {
email: string;
user: IUser,
providerAuthToken?: string;
}) => {
if (!providerAuthToken) {
@ -390,10 +405,7 @@ export const validateProviderAuthToken = async ({
jwt.verify(providerAuthToken, await getJwtProviderAuthSecret())
);
if (
decodedToken.authProvider !== user.authProvider ||
decodedToken.email !== email
) {
if (decodedToken.email !== email) {
throw new Error("Invalid authentication credentials.")
}
}
}

@ -115,5 +115,5 @@ export const updateSubscriptionOrgQuantity = async ({
);
}
await EELicenseService.refreshPlan(organizationId);
await EELicenseService.refreshPlan(new Types.ObjectId(organizationId));
};

@ -11,9 +11,9 @@ import {
IServiceTokenData,
Secret,
SecretBlindIndexData,
ServiceTokenData
ServiceTokenData,
} from "../models";
import { SecretVersion } from "../ee/models";
import { EventType, SecretVersion } from "../ee/models";
import {
BadRequestError,
InternalServerError,
@ -40,7 +40,7 @@ import {
} from "../utils/crypto";
import { TelemetryService } from "../services";
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
import { EELogService, EESecretService } from "../ee/services";
import { EEAuditLogService, EELogService, EESecretService } from "../ee/services";
import { getAuthDataPayloadIdObj, getAuthDataPayloadUserObj } from "../utils/auth";
import { getFolderByPath, getFolderIdFromServiceToken } from "../services/FolderService";
import picomatch from "picomatch";
@ -326,7 +326,8 @@ export const createSecretHelper = async ({
secretCommentCiphertext,
secretCommentIV,
secretCommentTag,
secretPath = "/"
secretPath = "/",
metadata
}: CreateSecretParams) => {
const secretBlindIndex = await generateSecretBlindIndexHelper({
secretName,
@ -433,10 +434,27 @@ export const createSecretHelper = async ({
...getAuthDataPayloadIdObj(authData),
workspaceId,
actions: [action],
channel: authData.authChannel,
ipAddress: authData.authIP
channel: authData.userAgentType,
ipAddress: authData.ipAddress
}));
await EEAuditLogService.createAuditLog(
authData,
{
type: EventType.CREATE_SECRET,
metadata: {
environment,
secretPath,
secretId: secret._id.toString(),
secretKey: secretName,
secretVersion: secret.version
}
},
{
workspaceId
}
);
// (EE) take a secret snapshot
await EESecretService.takeSecretSnapshot({
workspaceId,
@ -445,8 +463,8 @@ export const createSecretHelper = async ({
});
const postHogClient = await TelemetryService.getPostHogClient();
if (postHogClient) {
if (postHogClient && (metadata?.source !== "signup")) {
postHogClient.capture({
event: "secrets added",
distinctId: await TelemetryService.getDistinctId({
@ -457,8 +475,8 @@ export const createSecretHelper = async ({
environment,
workspaceId,
folderId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
channel: authData.userAgentType,
userAgent: authData.userAgent
}
});
}
@ -528,9 +546,24 @@ export const getSecretsHelper = async ({
...getAuthDataPayloadIdObj(authData),
workspaceId,
actions: [action],
channel: authData.authChannel,
ipAddress: authData.authIP
channel: authData.userAgentType,
ipAddress: authData.ipAddress
}));
await EEAuditLogService.createAuditLog(
authData,
{
type: EventType.GET_SECRETS,
metadata: {
environment,
secretPath,
numberOfSecrets: secrets.length
}
},
{
workspaceId
}
);
const postHogClient = await TelemetryService.getPostHogClient();
@ -545,8 +578,8 @@ export const getSecretsHelper = async ({
environment,
workspaceId,
folderId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
channel: authData.userAgentType,
userAgent: authData.userAgent
}
});
}
@ -622,10 +655,27 @@ export const getSecretHelper = async ({
...getAuthDataPayloadIdObj(authData),
workspaceId,
actions: [action],
channel: authData.authChannel,
ipAddress: authData.authIP
channel: authData.userAgentType,
ipAddress: authData.ipAddress
}));
await EEAuditLogService.createAuditLog(
authData,
{
type: EventType.GET_SECRET,
metadata: {
environment,
secretPath,
secretId: secret._id.toString(),
secretKey: secretName,
secretVersion: secret.version
}
},
{
workspaceId
}
);
const postHogClient = await TelemetryService.getPostHogClient();
if (postHogClient) {
@ -639,8 +689,8 @@ export const getSecretHelper = async ({
environment,
workspaceId,
folderId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
channel: authData.userAgentType,
userAgent: authData.userAgent
}
});
}
@ -771,9 +821,26 @@ export const updateSecretHelper = async ({
...getAuthDataPayloadIdObj(authData),
workspaceId,
actions: [action],
channel: authData.authChannel,
ipAddress: authData.authIP
channel: authData.userAgentType,
ipAddress: authData.ipAddress
}));
await EEAuditLogService.createAuditLog(
authData,
{
type: EventType.UPDATE_SECRET,
metadata: {
environment,
secretPath,
secretId: secret._id.toString(),
secretKey: secretName,
secretVersion: secret.version
}
},
{
workspaceId
}
);
// (EE) take a secret snapshot
await EESecretService.takeSecretSnapshot({
@ -795,8 +862,8 @@ export const updateSecretHelper = async ({
environment,
workspaceId,
folderId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
channel: authData.userAgentType,
userAgent: authData.userAgent
}
});
}
@ -894,10 +961,27 @@ export const deleteSecretHelper = async ({
...getAuthDataPayloadIdObj(authData),
workspaceId,
actions: [action],
channel: authData.authChannel,
ipAddress: authData.authIP
channel: authData.userAgentType,
ipAddress: authData.ipAddress
}));
await EEAuditLogService.createAuditLog(
authData,
{
type: EventType.DELETE_SECRET,
metadata: {
environment,
secretPath,
secretId: secret._id.toString(),
secretKey: secretName,
secretVersion: secret.version
}
},
{
workspaceId
}
);
// (EE) take a secret snapshot
await EESecretService.takeSecretSnapshot({
workspaceId,
@ -918,8 +1002,8 @@ export const deleteSecretHelper = async ({
environment,
workspaceId,
folderId,
channel: authData.authChannel,
userAgent: authData.authUserAgent
channel: authData.userAgentType,
userAgent: authData.userAgent
}
});
}

@ -1,3 +1,4 @@
import { Types } from "mongoose";
import {
Bot,
Key,
@ -25,7 +26,7 @@ export const createWorkspace = async ({
organizationId,
}: {
name: string;
organizationId: string;
organizationId: Types.ObjectId;
}) => {
// create workspace
const workspace = await new Workspace({

@ -245,7 +245,7 @@ const syncSecrets = async ({
integrationAuth,
integration,
secrets,
accessToken,
accessToken
});
break;
case INTEGRATION_BITBUCKET:
@ -1661,7 +1661,8 @@ const syncSecretsGitLab = async ({
const gitLabApiUrl = `${INTEGRATION_GITLAB_API_URL}/v4/projects/${integrationAppId}/variables`;
const headers = {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json"
"Accept-Encoding": "application/json",
"Content-Type": "application/json"
};
let allEnvVariables: GitLabSecret[] = [];
@ -1794,7 +1795,16 @@ const syncSecretsSupabase = async ({
const secretsToDelete: any = [];
getSecretsRes?.forEach((secretObj: any) => {
if (!(secretObj.name in secrets)) {
if (
!(secretObj.name in secrets) &&
// supbase reserved secret ref: https://supabase.com/docs/guides/functions/secrets#default-secrets
![
"SUPABASE_ANON_KEY",
"SUPABASE_SERVICE_ROLE_KEY",
"SUPABASE_DB_URL",
"SUPABASE_URL"
].includes(secretObj.name)
) {
secretsToDelete.push(secretObj.name);
}
});
@ -2019,7 +2029,7 @@ const syncSecretsTeamCity = async ({
integrationAuth,
integration,
secrets,
accessToken,
accessToken
}: {
integrationAuth: IIntegrationAuth;
integration: IIntegration;
@ -2034,26 +2044,21 @@ const syncSecretsTeamCity = async ({
// get secrets from Teamcity
const res = (
await standardRequest.get(
`${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters`,
`${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
},
})
)
.data
.property
.reduce(
(obj: any, secret: TeamCitySecret) => {
const secretName = secret.name.replace(/^env\./, "");
return ({
...obj,
[secretName]: secret.value
})
},
{}
);
Accept: "application/json"
}
}
)
).data.property.reduce((obj: any, secret: TeamCitySecret) => {
const secretName = secret.name.replace(/^env\./, "");
return {
...obj,
[secretName]: secret.value
};
}, {});
for await (const key of Object.keys(secrets)) {
if (!(key in res) || (key in res && secrets[key] !== res[key])) {

@ -1,15 +1,31 @@
import { Types } from "mongoose";
import {
IServiceAccount,
IServiceTokenData,
IUser,
} from "../../models";
import {
ServiceActor,
UserActor,
UserAgentType
} from "../../ee/models";
export interface AuthData {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
authChannel: string;
authIP: string;
authUserAgent: string;
interface BaseAuthData {
ipAddress: string;
userAgent: string;
userAgentType: UserAgentType;
tokenVersionId?: Types.ObjectId;
}
}
export interface UserAuthData extends BaseAuthData {
actor: UserActor;
authPayload: IUser;
}
export interface ServiceTokenAuthData extends BaseAuthData {
actor: ServiceActor;
authPayload: IServiceTokenData;
}
export type AuthData =
| UserAuthData
| ServiceTokenAuthData;

@ -17,6 +17,9 @@ export interface CreateSecretParams {
secretCommentIV?: string;
secretCommentTag?: string;
secretPath: string;
metadata?: {
source?: string;
}
}
export interface GetSecretsParams {

@ -1,25 +1,13 @@
import jwt from "jsonwebtoken";
import { Types } from "mongoose";
import { NextFunction, Request, Response } from "express";
import {
getAuthAPIKeyPayload,
getAuthSAAKPayload,
getAuthSTDPayload,
getAuthUserPayload,
validateAuthMode,
} from "../helpers/auth";
import {
IServiceAccount,
IServiceTokenData,
IUser,
} from "../models";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../variables";
import { getChannelFromUserAgent } from "../utils/posthog";
import { AuthMode } from "../variables";
import { AuthData } from "../interfaces/middleware";
declare module "jsonwebtoken" {
export interface UserIDJwtPayload extends jwt.JwtPayload {
@ -38,9 +26,9 @@ declare module "jsonwebtoken" {
* @returns
*/
const requireAuth = ({
acceptedAuthModes = [AUTH_MODE_JWT],
acceptedAuthModes = [AuthMode.JWT],
}: {
acceptedAuthModes: string[];
acceptedAuthModes: AuthMode[];
}) => {
return async (req: Request, res: Response, next: NextFunction) => {
@ -50,55 +38,36 @@ const requireAuth = ({
headers: req.headers,
acceptedAuthModes,
});
let authPayload: IUser | IServiceAccount | IServiceTokenData;
let authUserPayload: {
user: IUser;
tokenVersionId: Types.ObjectId;
};
let authData: AuthData;
switch (authMode) {
case AUTH_MODE_SERVICE_ACCOUNT:
authPayload = await getAuthSAAKPayload({
authTokenValue,
});
req.serviceAccount = authPayload;
break;
case AUTH_MODE_SERVICE_TOKEN:
authPayload = await getAuthSTDPayload({
case AuthMode.SERVICE_TOKEN:
authData = await getAuthSTDPayload({
req,
authTokenValue,
});
req.serviceTokenData = authPayload;
req.serviceTokenData = authData.authPayload;
break;
case AUTH_MODE_API_KEY:
authPayload = await getAuthAPIKeyPayload({
authTokenValue,
case AuthMode.API_KEY:
authData = await getAuthAPIKeyPayload({
req,
authTokenValue
});
req.user = authPayload;
req.user = authData.authPayload;
break;
default:
authUserPayload = await getAuthUserPayload({
authTokenValue,
case AuthMode.JWT:
authData = await getAuthUserPayload({
req,
authTokenValue
});
authPayload = authUserPayload.user;
req.user = authUserPayload.user;
req.tokenVersionId = authUserPayload.tokenVersionId;
// authPayload = authUserPayload.user;
req.user = authData.authPayload;
// req.tokenVersionId = authUserPayload.tokenVersionId; // TODO
break;
}
req.requestData = {
...req.params,
...req.query,
...req.body,
}
req.authData = {
authMode,
authPayload, // User, ServiceAccount, ServiceTokenData
authChannel: getChannelFromUserAgent(req.headers["user-agent"]),
authIP: req.realIP,
authUserAgent: req.headers["user-agent"] ?? "other",
tokenVersionId: req.tokenVersionId,
}
req.authData = authData;
return next();
}

@ -10,6 +10,8 @@ import Membership, { IMembership } from "./membership";
import MembershipOrg, { IMembershipOrg } from "./membershipOrg";
import Organization, { IOrganization } from "./organization";
import Secret, { ISecret } from "./secret";
import Folder, { TFolderRootSchema, TFolderSchema } from "./folder";
import SecretImport, { ISecretImports } from "./secretImports";
import SecretBlindIndexData, { ISecretBlindIndexData } from "./secretBlindIndexData";
import ServiceToken, { IServiceToken } from "./serviceToken";
import ServiceAccount, { IServiceAccount } from "./serviceAccount"; // new
@ -17,7 +19,7 @@ import ServiceAccountKey, { IServiceAccountKey } from "./serviceAccountKey"; //
import ServiceAccountOrganizationPermission, { IServiceAccountOrganizationPermission } from "./serviceAccountOrganizationPermission"; // new
import ServiceAccountWorkspacePermission, { IServiceAccountWorkspacePermission } from "./serviceAccountWorkspacePermission"; // new
import TokenData, { ITokenData } from "./tokenData";
import User, { AuthProvider, IUser } from "./user";
import User, { AuthMethod, IUser } from "./user";
import UserAction, { IUserAction } from "./userAction";
import Workspace, { IWorkspace } from "./workspace";
import ServiceTokenData, { IServiceTokenData } from "./serviceTokenData";
@ -26,7 +28,7 @@ import LoginSRPDetail, { ILoginSRPDetail } from "./loginSRPDetail";
import TokenVersion, { ITokenVersion } from "./tokenVersion";
export {
AuthProvider,
AuthMethod,
BackupPrivateKey,
IBackupPrivateKey,
Bot,
@ -51,6 +53,11 @@ export {
IOrganization,
Secret,
ISecret,
Folder,
TFolderRootSchema,
TFolderSchema,
SecretImport,
ISecretImports,
SecretBlindIndexData,
ISecretBlindIndexData,
ServiceToken,

@ -1,6 +1,6 @@
import { Document, Schema, Types, model } from "mongoose";
export enum AuthProvider {
export enum AuthMethod {
EMAIL = "email",
GOOGLE = "google",
GITHUB = "github",
@ -11,8 +11,8 @@ export enum AuthProvider {
export interface IUser extends Document {
_id: Types.ObjectId;
authId?: string;
authProvider?: AuthProvider;
authProvider?: AuthMethod;
authMethods: AuthMethod[];
email: string;
firstName?: string;
lastName?: string;
@ -36,12 +36,17 @@ export interface IUser extends Document {
const userSchema = new Schema<IUser>(
{
authId: {
authProvider: { // TODO field: deprecate
type: String,
enum: AuthMethod,
},
authProvider: {
type: String,
enum: AuthProvider,
authMethods: {
type: [{
type: String,
enum: AuthMethod,
}],
default: [AuthMethod.EMAIL],
required: true
},
email: {
type: String,

@ -6,13 +6,21 @@ const router = express.Router();
router.get(
"/status",
async (req: Request, res: Response) => {
const gitAppId = await getSecretScanningGitAppId()
const gitSecretScanningWebhookSecret = await getSecretScanningWebhookSecret()
const gitSecretScanningPrivateKey = await getSecretScanningPrivateKey()
let secretScanningConfigured = false
if (gitAppId && gitSecretScanningPrivateKey && gitSecretScanningWebhookSecret) {
secretScanningConfigured = true
}
res.status(200).json({
date: new Date(),
message: "Ok",
emailConfigured: await getSmtpConfigured(),
secretScanningConfigured: await getSecretScanningGitAppId() && await getSecretScanningWebhookSecret() && await getSecretScanningPrivateKey(),
inviteOnlySignup: await getInviteOnlySignup(),
redisConfigured: await getRedisUrl() !== "" && await getRedisUrl() !== undefined
secretScanningConfigured: secretScanningConfigured,
})
}
);

@ -4,11 +4,11 @@ import { body } from "express-validator";
import { requireAuth, validateRequest } from "../../middleware";
import { authController } from "../../controllers/v1";
import { authLimiter } from "../../helpers/rateLimiter";
import { AUTH_MODE_JWT } from "../../variables";
import { AuthMode } from "../../variables";
router.post("/token", validateRequest, authController.getNewToken);
router.post( // deprecated (moved to api/v2/auth/login1)
router.post( // TODO endpoint: deprecate (moved to api/v3/auth/login1)
"/login1",
authLimiter,
body("email").exists().trim().notEmpty(),
@ -17,7 +17,7 @@ router.post( // deprecated (moved to api/v2/auth/login1)
authController.login1
);
router.post( // deprecated (moved to api/v2/auth/login2)
router.post( // TODO endpoint: deprecate (moved to api/v3/auth/login2)
"/login2",
authLimiter,
body("email").exists().trim().notEmpty(),
@ -30,7 +30,7 @@ router.post(
"/logout",
authLimiter,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
authController.logout
);
@ -38,7 +38,7 @@ router.post(
router.post(
"/checkAuth",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
authController.checkAuth
);
@ -49,13 +49,13 @@ router.get(
authController.getCommonPasswords
);
router.delete(
router.delete( // TODO endpoint: deprecate (moved to DELETE v2/users/me/sessions)
"/sessions",
authLimiter,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
authController.revokeAllSessions
);
export default router;
export default router;

@ -8,12 +8,12 @@ import {
validateRequest,
} from "../../middleware";
import { botController } from "../../controllers/v1";
import { ADMIN, AUTH_MODE_JWT, MEMBER } from "../../variables";
import { ADMIN, AuthMode, MEMBER } from "../../variables";
router.get(
"/:workspaceId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -27,7 +27,7 @@ router.get(
router.patch(
"/:botId/active",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireBotAuth({
acceptedRoles: [ADMIN, MEMBER],

@ -8,9 +8,8 @@ import {
} from "../../middleware";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
MEMBER,
AuthMode,
MEMBER
} from "../../variables";
import { body, param } from "express-validator";
import { integrationController } from "../../controllers/v1";
@ -18,7 +17,7 @@ import { integrationController } from "../../controllers/v1";
router.post(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -44,7 +43,7 @@ router.post(
router.patch(
"/:integrationId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
requireIntegrationAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -64,7 +63,7 @@ router.patch(
router.delete(
"/:integrationId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
requireIntegrationAuth({
acceptedRoles: [ADMIN, MEMBER],

@ -9,16 +9,15 @@ import {
} from "../../middleware";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
MEMBER,
AuthMode,
MEMBER
} from "../../variables";
import { integrationAuthController } from "../../controllers/v1";
router.get(
"/integration-options",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
integrationAuthController.getIntegrationOptions
);
@ -26,7 +25,7 @@ router.get(
router.get(
"/:integrationAuthId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -39,7 +38,7 @@ router.get(
router.post(
"/oauth-token",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -62,7 +61,7 @@ router.post(
body("integration").exists().trim().notEmpty(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -74,7 +73,7 @@ router.post(
router.get(
"/:integrationAuthId/apps",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -89,7 +88,7 @@ router.get(
router.get(
"/:integrationAuthId/teams",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -102,7 +101,7 @@ router.get(
router.get(
"/:integrationAuthId/vercel/branches",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -117,7 +116,7 @@ router.get(
router.get(
"/:integrationAuthId/railway/environments",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -131,7 +130,7 @@ router.get(
router.get(
"/:integrationAuthId/railway/services",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -145,7 +144,7 @@ router.get(
router.get(
"/:integrationAuthId/bitbucket/workspaces",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -158,7 +157,7 @@ router.get(
router.get(
"/:integrationAuthId/northflank/secret-groups",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -172,7 +171,7 @@ router.get(
router.delete(
"/:integrationAuthId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],

@ -3,12 +3,14 @@ const router = express.Router();
import { body } from "express-validator";
import { requireAuth, validateRequest } from "../../middleware";
import { membershipOrgController } from "../../controllers/v1";
import { AUTH_MODE_JWT } from "../../variables";
import { AuthMode } from "../../variables";
// TODO endpoint: consider moving these endpoints to be under /organization to be more RESTful
router.post(
"/signup",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("inviteeEmail").exists().trim().notEmpty().isEmail(),
body("organizationId").exists().trim().notEmpty(),

@ -6,13 +6,15 @@ import {
validateRequest,
} from "../../middleware";
import { body, param } from "express-validator";
import { ADMIN, AUTH_MODE_JWT, MEMBER } from "../../variables";
import { ADMIN, AuthMode, MEMBER } from "../../variables";
import { keyController } from "../../controllers/v1";
// TODO endpoint: consider moving these endpoints to be under /workspaces to be more RESTful
router.post(
"/:workspaceId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -24,10 +26,10 @@ router.post(
keyController.uploadKey
);
router.get(
router.get( // TODO endpoint: deprecate (note: move frontend to v2/workspace/key or something)
"/:workspaceId/latest",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],

@ -4,44 +4,45 @@ import { body, param } from "express-validator";
import { requireAuth, validateRequest } from "../../middleware";
import { membershipController } from "../../controllers/v1";
import { membershipController as EEMembershipControllers } from "../../ee/controllers/v1";
import { AUTH_MODE_JWT } from "../../variables";
import { AuthMode } from "../../variables";
// note: ALL DEPRECIATED (moved to api/v2/workspace/:workspaceId/memberships/:membershipId)
// TODO endpoint: consider moving these endpoints to be under /workspace to be more RESTful
router.get( // used for old CLI (deprecate)
router.get( // TODO endpoint: deprecate - used for old CLI (deprecate)
"/:workspaceId/connect",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("workspaceId").exists().trim(),
validateRequest,
membershipController.validateMembership
);
router.delete(
router.delete( // TODO endpoint: check dashboard
"/:membershipId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("membershipId").exists().trim(),
validateRequest,
membershipController.deleteMembership
);
router.post(
router.post( // TODO endpoint: check dashboard
"/:membershipId/change-role",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("role").exists().trim(),
validateRequest,
membershipController.changeMembershipRole
);
router.post(
router.post( // TODO endpoint: check dashboard
"/:membershipId/deny-permissions",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("membershipId").isMongoId().exists().trim(),
body("permissions").isArray().exists(),

@ -3,13 +3,12 @@ const router = express.Router();
import { param } from "express-validator";
import { requireAuth, validateRequest } from "../../middleware";
import { membershipOrgController } from "../../controllers/v1";
import { AUTH_MODE_JWT } from "../../variables";
import { AuthMode } from "../../variables";
router.post(
// TODO
router.post( // TODO endpoint: check dashboard
"/membershipOrg/:membershipOrgId/change-role",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("membershipOrgId"),
validateRequest,
@ -17,9 +16,9 @@ router.post(
);
router.delete(
"/:membershipOrgId",
"/:membershipOrgId", // TODO endpoint: check dashboard
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("membershipOrgId").exists().trim(),
validateRequest,

@ -9,16 +9,16 @@ import {
import {
ACCEPTED,
ADMIN,
AUTH_MODE_JWT,
AuthMode,
MEMBER,
OWNER,
OWNER
} from "../../variables";
import { organizationController } from "../../controllers/v1";
router.get( // deprecated (moved to api/v2/users/me/organizations)
router.get( // TODO endpoint: deprecate (moved to api/v2/users/me/organizations)
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
organizationController.getOrganizations
);
@ -26,7 +26,7 @@ router.get( // deprecated (moved to api/v2/users/me/organizations)
router.post( // not used on frontend
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("organizationName").exists().trim().notEmpty(),
validateRequest,
@ -36,7 +36,7 @@ router.post( // not used on frontend
router.get(
"/:organizationId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -47,10 +47,10 @@ router.get(
organizationController.getOrganization
);
router.get( // deprecated (moved to api/v2/organizations/:organizationId/memberships)
router.get( // TODO endpoint: deprecate (moved to api/v2/organizations/:organizationId/memberships)
"/:organizationId/users",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -61,10 +61,10 @@ router.get( // deprecated (moved to api/v2/organizations/:organizationId/members
organizationController.getOrganizationMembers
);
router.get(
router.get( // TODO endpoint: move to /v2/users/me/organizations/:organizationId/workspaces
"/:organizationId/my-workspaces", // deprecated (moved to api/v2/organizations/:organizationId/workspaces)
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -78,7 +78,7 @@ router.get(
router.patch(
"/:organizationId/name",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -93,7 +93,7 @@ router.patch(
router.get(
"/:organizationId/incidentContactOrg",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -107,7 +107,7 @@ router.get(
router.post(
"/:organizationId/incidentContactOrg",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -122,7 +122,7 @@ router.post(
router.delete(
"/:organizationId/incidentContactOrg",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -135,9 +135,9 @@ router.delete(
);
router.post(
"/:organizationId/customer-portal-session",
"/:organizationId/customer-portal-session", // TODO endpoint: move to EE
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -148,24 +148,10 @@ router.post(
organizationController.createOrganizationPortalSession
);
router.get(
"/:organizationId/subscriptions",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
}),
param("organizationId").exists().trim(),
validateRequest,
organizationController.getOrganizationSubscriptions
);
router.get(
"/:organizationId/workspace-memberships",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],

@ -4,14 +4,12 @@ import { body } from "express-validator";
import { requireAuth, requireSignupAuth, validateRequest } from "../../middleware";
import { passwordController } from "../../controllers/v1";
import { passwordLimiter } from "../../helpers/rateLimiter";
import {
AUTH_MODE_JWT,
} from "../../variables";
import { AuthMode } from "../../variables";
router.post(
"/srp1",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("clientPublicKey").exists().isString().trim().notEmpty(),
validateRequest,
@ -22,7 +20,7 @@ router.post(
"/change-password",
passwordLimiter,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("clientProof").exists().trim().notEmpty(),
body("protectedKey").exists().isString().trim().notEmpty(),
@ -65,7 +63,7 @@ router.post(
"/backup-private-key",
passwordLimiter,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("clientProof").exists().isString().trim().notEmpty(),
body("encryptedPrivateKey").exists().isString().trim().notEmpty(), // (backup) private key encrypted under a strong key

@ -10,16 +10,16 @@ import { body, param, query } from "express-validator";
import { secretController } from "../../controllers/v1";
import {
ADMIN,
AUTH_MODE_JWT,
MEMBER,
AuthMode,
MEMBER
} from "../../variables";
// note to devs: these endpoints will be deprecated in favor of v2
// note: endpoints deprecated in favor of v3/secrets
router.post(
router.post( // TODO endpoint: deprecate (moved to POST api/v3/secrets)
"/:workspaceId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -34,10 +34,10 @@ router.post(
secretController.pushSecrets
);
router.get(
router.get( // TODO endpoint: deprecate (moved to GET api/v3/secrets)
"/:workspaceId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -50,7 +50,7 @@ router.get(
secretController.pullSecrets
);
router.get(
router.get( // TODO endpoint: deprecate (moved to GET api/v3/secrets)
"/:workspaceId/service-token",
requireServiceTokenAuth,
query("environment").exists().trim(),

@ -1,14 +1,14 @@
import express from "express";
const router = express.Router();
import { body, param, query } from "express-validator";
import { secretImportController } from "../../controllers/v1";
import { requireAuth, requireWorkspaceAuth, validateRequest } from "../../middleware";
import { ADMIN, AUTH_MODE_JWT, MEMBER } from "../../variables";
import { ADMIN, AuthMode, MEMBER } from "../../variables";
const router = express.Router();
router.post(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -27,7 +27,7 @@ router.post(
router.put(
"/:id",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
}),
param("id").exists().isString().trim(),
body("secretImports").exists().isArray(),
@ -40,7 +40,7 @@ router.put(
router.delete(
"/:id",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
}),
param("id").exists().isString().trim(),
body("secretImportPath").isString().exists().trim(),
@ -52,7 +52,7 @@ router.delete(
router.get(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -68,7 +68,7 @@ router.get(
router.get(
"/secrets",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],

@ -1,27 +1,23 @@
import express from "express";
const router = express.Router();
import {
requireAuth,
requireWorkspaceAuth,
validateRequest,
} from "../../middleware";
import { body, param, query } from "express-validator";
import {
createFolder,
deleteFolder,
getFolders,
updateFolderById,
updateFolderById
} from "../../controllers/v1/secretsFolderController";
import { ADMIN, MEMBER } from "../../variables";
import { requireAuth, requireWorkspaceAuth, validateRequest } from "../../middleware";
import { ADMIN, AuthMode, MEMBER } from "../../variables";
const router = express.Router();
router.post(
"/",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: "body",
locationWorkspaceId: "body"
}),
body("workspaceId").exists(),
body("environment").exists(),
@ -34,7 +30,7 @@ router.post(
router.patch(
"/:folderId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
}),
body("workspaceId").exists(),
body("environment").exists(),
@ -46,7 +42,7 @@ router.patch(
router.delete(
"/:folderId",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
}),
body("workspaceId").exists(),
body("environment").exists(),
@ -58,7 +54,7 @@ router.delete(
router.get(
"/",
requireAuth({
acceptedAuthModes: ["jwt"],
acceptedAuthModes: [AuthMode.JWT,AuthMode.SERVICE_TOKEN]
}),
query("workspaceId").exists().isString().trim(),
query("environment").exists().isString().trim(),

@ -9,23 +9,23 @@ import {
import { body } from "express-validator";
import {
ADMIN,
AUTH_MODE_JWT,
MEMBER,
AuthMode,
MEMBER
} from "../../variables";
import { serviceTokenController } from "../../controllers/v1";
// note: deprecate service-token routes in favor of service-token data routes/structure
router.get(
router.get( // TODO endpoint: deprecate
"/",
requireServiceTokenAuth,
serviceTokenController.getServiceToken
);
router.post(
router.post( // TODO endpoint: deprecate
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],

@ -5,7 +5,9 @@ import { validateRequest } from "../../middleware";
import { signupController } from "../../controllers/v1";
import { authLimiter } from "../../helpers/rateLimiter";
router.post(
// TODO: consider moving to users/v3/signup
router.post( // TODO endpoint: consider moving to v3/users/signup/mail
"/email/signup",
authLimiter,
body("email").exists().trim().notEmpty().isEmail(),
@ -14,7 +16,7 @@ router.post(
);
router.post(
"/email/verify",
"/email/verify", // TODO endpoint: consider moving to v3/users/signup/verify
authLimiter,
body("email").exists().trim().notEmpty().isEmail(),
body("code").exists().trim().notEmpty(),
@ -22,4 +24,4 @@ router.post(
signupController.verifyEmailSignup
);
export default router;
export default router;

@ -2,14 +2,12 @@ import express from "express";
const router = express.Router();
import { requireAuth } from "../../middleware";
import { userController } from "../../controllers/v1";
import {
AUTH_MODE_JWT,
} from "../../variables";
import { AuthMode } from "../../variables";
router.get(
router.get( // TODO endpoint: deprecate (moved to v2/users/me)
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
userController.getUser
);

@ -3,13 +3,13 @@ const router = express.Router();
import { requireAuth, validateRequest } from "../../middleware";
import { body, query } from "express-validator";
import { userActionController } from "../../controllers/v1";
import { AUTH_MODE_JWT } from "../../variables";
import { AuthMode } from "../../variables";
// note: [userAction] will be deprecated in /v2 in favor of [action]
router.post(
router.post( // TODO endpoint: move this into /users/me
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("action"),
validateRequest,
@ -19,7 +19,7 @@ router.post(
router.get(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
query("action"),
validateRequest,

@ -2,13 +2,13 @@ import express from "express";
const router = express.Router();
import { requireAuth, requireWorkspaceAuth, validateRequest } from "../../middleware";
import { body, param, query } from "express-validator";
import { ADMIN, AUTH_MODE_JWT, MEMBER } from "../../variables";
import { ADMIN, AuthMode, MEMBER } from "../../variables";
import { webhookController } from "../../controllers/v1";
router.post(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -27,7 +27,7 @@ router.post(
router.patch(
"/:webhookId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT],
}),
param("webhookId").exists().isString().trim(),
body("isDisabled").default(false).isBoolean(),
@ -38,7 +38,7 @@ router.patch(
router.post(
"/:webhookId/test",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT],
}),
param("webhookId").exists().isString().trim(),
validateRequest,
@ -48,7 +48,7 @@ router.post(
router.delete(
"/:webhookId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT],
}),
param("webhookId").exists().isString().trim(),
validateRequest,
@ -58,7 +58,7 @@ router.delete(
router.get(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],

@ -8,16 +8,15 @@ import {
} from "../../middleware";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
MEMBER,
AuthMode,
MEMBER
} from "../../variables";
import { membershipController, workspaceController } from "../../controllers/v1";
router.get(
"/:workspaceId/keys",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -31,7 +30,7 @@ router.get(
router.get(
"/:workspaceId/users",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -45,7 +44,7 @@ router.get(
router.get(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
workspaceController.getWorkspaces
);
@ -53,7 +52,7 @@ router.get(
router.get(
"/:workspaceId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -67,7 +66,7 @@ router.get(
router.post(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("workspaceName").exists().trim().notEmpty(),
body("organizationId").exists().trim().notEmpty(),
@ -78,7 +77,7 @@ router.post(
router.delete(
"/:workspaceId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@ -92,7 +91,7 @@ router.delete(
router.post(
"/:workspaceId/name",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -107,7 +106,7 @@ router.post(
router.post(
"/:workspaceId/invite-signup",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -122,7 +121,7 @@ router.post(
router.get(
"/:workspaceId/integrations",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -136,7 +135,7 @@ router.get(
router.get(
"/:workspaceId/authorizations",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -148,9 +147,9 @@ router.get(
);
router.get(
"/:workspaceId/service-tokens", // deprecate
"/:workspaceId/service-tokens", // TODO endpoint: deprecate
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],

@ -5,7 +5,7 @@ import { requireMfaAuth, validateRequest } from "../../middleware";
import { authController } from "../../controllers/v2";
import { authLimiter } from "../../helpers/rateLimiter";
router.post(
router.post( // TODO: deprecate (moved to api/v3/auth/login1)
"/login1",
authLimiter,
body("email").isString().trim().notEmpty(),
@ -14,7 +14,7 @@ router.post(
authController.login1
);
router.post(
router.post( // TODO: deprecate (moved to api/v3/auth/login1)
"/login2",
authLimiter,
body("email").isString().trim().notEmpty(),

@ -9,14 +9,14 @@ import {
} from "../../middleware";
import {
ADMIN,
AUTH_MODE_JWT,
MEMBER,
AuthMode,
MEMBER
} from "../../variables";
router.post(
"/:workspaceId/environments",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -32,7 +32,7 @@ router.post(
router.put(
"/:workspaceId/environments",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -46,10 +46,28 @@ router.put(
environmentController.renameWorkspaceEnvironment
);
router.patch(
"/:workspaceId/environments",
requireAuth({
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: "params",
}),
param("workspaceId").exists().trim(),
body("environmentSlug").exists().isString().trim(),
body("environmentName").exists().isString().trim(),
body("otherEnvironmentSlug").exists().isString().trim(),
body("otherEnvironmentName").exists().isString().trim(),
validateRequest,
environmentController.reorderWorkspaceEnvironments
);
router.delete(
"/:workspaceId/environments",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@ -64,7 +82,7 @@ router.delete(
router.get(
"/:workspaceId/environments",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [MEMBER, ADMIN],

@ -10,10 +10,9 @@ import { body, param } from "express-validator";
import {
ACCEPTED,
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AuthMode,
MEMBER,
OWNER,
OWNER
} from "../../variables";
import { organizationsController } from "../../controllers/v2";
@ -24,7 +23,7 @@ router.get(
param("organizationId").exists().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
@ -40,7 +39,7 @@ router.patch(
body("role").exists().isString().trim().isIn([OWNER, ADMIN, MEMBER]),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
@ -59,7 +58,7 @@ router.delete(
param("membershipId").exists().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
@ -77,7 +76,7 @@ router.get(
param("organizationId").exists().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],
@ -86,12 +85,12 @@ router.get(
organizationsController.getOrganizationWorkspaces
);
router.get(
router.get( // TODO endpoint: deprecate service accounts
"/:organizationId/service-accounts",
param("organizationId").exists().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN],

@ -1,4 +1,5 @@
import express from "express";
const router = express.Router();
import {
requireAuth,
requireSecretAuth,
@ -8,8 +9,7 @@ import {
import { body, param, query } from "express-validator";
import {
ADMIN,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_TOKEN,
AuthMode,
MEMBER,
PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS,
@ -17,14 +17,12 @@ import {
import { CreateSecretRequestBody, ModifySecretRequestBody } from "../../types/secret";
import { secretController } from "../../controllers/v2";
// note to devs: stop supporting these routes [deprecated]
const router = express.Router();
// note: endpoints deprecated in favor of v3/secrets
router.post(
router.post( // TODO endpoint: deprecate (moved to POST api/v3/secrets)
"/batch-create/workspace/:workspaceId/environment/:environment",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -39,9 +37,9 @@ router.post(
);
router.post(
"/workspace/:workspaceId/environment/:environment",
"/workspace/:workspaceId/environment/:environment", // TODO endpoint: deprecate (moved to POST api/v3/secrets)
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -55,12 +53,12 @@ router.post(
secretController.createSecret
);
router.get(
router.get( // TODO endpoint: deprecate (moved to GET api/v3/secrets)
"/workspace/:workspaceId",
param("workspaceId").exists().trim(),
query("environment").exists(),
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_TOKEN],
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -71,10 +69,10 @@ router.get(
secretController.getSecrets
);
router.get(
router.get( // TODO endpoint: deprecate (moved to POST api/v3/secrets)
"/:secretId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_TOKEN],
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN],
}),
requireSecretAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -84,10 +82,10 @@ router.get(
secretController.getSecret
);
router.delete(
router.delete( // TODO endpoint: deprecate (moved to DELETE api/v3/secrets)
"/batch/workspace/:workspaceId/environment/:environmentName",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("workspaceId").exists().isMongoId().trim(),
param("environmentName").exists().trim(),
@ -100,10 +98,10 @@ router.delete(
secretController.deleteSecrets
);
router.delete(
router.delete( // TODO endpoint: deprecate (moved to DELETE api/v3/secrets)
"/:secretId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireSecretAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -114,10 +112,10 @@ router.delete(
secretController.deleteSecret
);
router.patch(
router.patch( // TODO endpoint: deprecate (moved to PATCH api/v3/secrets)
"/batch-modify/workspace/:workspaceId/environment/:environmentName",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("secrets").exists().isArray().custom((secrets: ModifySecretRequestBody[]) => secrets.length > 0),
param("workspaceId").exists().isMongoId().trim(),
@ -130,10 +128,10 @@ router.patch(
secretController.updateSecrets
);
router.patch(
router.patch( // TODO endpoint: deprecate (moved to PATCH api/v3/secrets)
"/workspace/:workspaceId/environment/:environmentName",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
body("secret").isObject(),
param("workspaceId").exists().isMongoId().trim(),

@ -12,10 +12,7 @@ import { body, query } from "express-validator";
import { secretsController } from "../../controllers/v2";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
AuthMode,
MEMBER,
PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS,
@ -24,10 +21,10 @@ import {
} from "../../variables";
import { BatchSecretRequest } from "../../types/secret";
router.post(
router.post( // TODO endpoint: strongly consider deprecation in favor of a single operation experience on dashboard
"/batch",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY, AUTH_MODE_SERVICE_TOKEN]
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -59,7 +56,7 @@ router.post(
secretsController.batchSecrets
);
router.post(
router.post( // TODO endpoint: deprecate (moved to POST api/v3/secrets)
"/",
body("workspaceId").exists().isString().trim(),
body("environment").exists().isString().trim(),
@ -109,7 +106,7 @@ router.post(
}),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY, AUTH_MODE_SERVICE_TOKEN]
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -120,7 +117,7 @@ router.post(
secretsController.createSecrets
);
router.get(
router.get( // TODO endpoint: deprecate (moved to GET api/v3/secrets)
"/",
query("workspaceId").exists().trim(),
query("environment").exists().trim(),
@ -130,12 +127,7 @@ router.get(
query("include_imports").optional().default(false).isBoolean(),
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
]
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -146,7 +138,7 @@ router.get(
secretsController.getSecrets
);
router.patch(
router.patch( // TODO endpoint: deprecate (moved to PATCH api/v3/secrets)
"/",
body("secrets")
.exists()
@ -172,7 +164,7 @@ router.patch(
}),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY, AUTH_MODE_SERVICE_TOKEN]
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
}),
requireSecretsAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -181,7 +173,7 @@ router.patch(
secretsController.updateSecrets
);
router.delete(
router.delete( // TODO endpoint: deprecate (moved to DELETE api/v3/secrets)
"/",
body("secretIds")
.exists()
@ -201,7 +193,7 @@ router.delete(
.isEmpty(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY, AUTH_MODE_SERVICE_TOKEN]
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN]
}),
requireSecretsAuth({
acceptedRoles: [ADMIN, MEMBER],

@ -1,159 +1,161 @@
import express from "express";
const router = express.Router();
import {
requireAuth,
requireOrganizationAuth,
requireServiceAccountAuth,
requireServiceAccountWorkspacePermissionAuth,
requireWorkspaceAuth,
validateRequest,
} from "../../middleware";
import { body, param, query } from "express-validator";
import {
ACCEPTED,
ADMIN,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
MEMBER,
OWNER,
} from "../../variables";
import { serviceAccountsController } from "../../controllers/v2";
router.get( // TODO: check
"/me",
requireAuth({
acceptedAuthModes: [AUTH_MODE_SERVICE_ACCOUNT],
}),
serviceAccountsController.getCurrentServiceAccount
);
// TODO endpoint: deprecate all
router.get(
"/:serviceAccountId",
param("serviceAccountId").exists().isString().trim(),
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
serviceAccountsController.getServiceAccountById
);
// import {
// requireAuth,
// requireOrganizationAuth,
// requireServiceAccountAuth,
// requireServiceAccountWorkspacePermissionAuth,
// requireWorkspaceAuth,
// validateRequest,
// } from "../../middleware";
// import { body, param, query } from "express-validator";
// import {
// ACCEPTED,
// ADMIN,
// MEMBER,
// OWNER,
// AuthMode
// } from "../../variables";
// import { serviceAccountsController } from "../../controllers/v2";
router.post(
"/",
body("organizationId").exists().isString().trim(),
body("name").exists().isString().trim(),
body("publicKey").exists().isString().trim(),
body("expiresIn").isNumeric(), // measured in ms
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED],
locationOrganizationId: "body",
}),
serviceAccountsController.createServiceAccount
);
// router.get( // TODO: check
// "/me",
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_SERVICE_ACCOUNT],
// }),
// serviceAccountsController.getCurrentServiceAccount
// );
router.patch(
"/:serviceAccountId/name",
param("serviceAccountId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
serviceAccountsController.changeServiceAccountName
);
// router.get(
// "/:serviceAccountId",
// param("serviceAccountId").exists().isString().trim(),
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// serviceAccountsController.getServiceAccountById
// );
router.delete(
"/:serviceAccountId",
param("serviceAccountId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
serviceAccountsController.deleteServiceAccount
);
// router.post(
// "/",
// body("organizationId").exists().isString().trim(),
// body("name").exists().isString().trim(),
// body("publicKey").exists().isString().trim(),
// body("expiresIn").isNumeric(), // measured in ms
// validateRequest,
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireOrganizationAuth({
// acceptedRoles: [OWNER, ADMIN, MEMBER],
// acceptedStatuses: [ACCEPTED],
// locationOrganizationId: "body",
// }),
// serviceAccountsController.createServiceAccount
// );
router.get(
"/:serviceAccountId/permissions/workspace",
param("serviceAccountId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
serviceAccountsController.getServiceAccountWorkspacePermissions
);
// router.patch(
// "/:serviceAccountId/name",
// param("serviceAccountId").exists().isString().trim(),
// validateRequest,
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// serviceAccountsController.changeServiceAccountName
// );
router.post(
"/:serviceAccountId/permissions/workspace",
param("serviceAccountId").exists().isString().trim(),
body("workspaceId").exists().isString().notEmpty(),
body("environment").exists().isString().notEmpty(),
body("read").isBoolean().optional(),
body("write").isBoolean().optional(),
body("encryptedKey").exists().isString().notEmpty(),
body("nonce").exists().isString().notEmpty(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: "body",
}),
serviceAccountsController.addServiceAccountWorkspacePermission
);
// router.delete(
// "/:serviceAccountId",
// param("serviceAccountId").exists().isString().trim(),
// validateRequest,
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// serviceAccountsController.deleteServiceAccount
// );
router.delete(
"/:serviceAccountId/permissions/workspace/:serviceAccountWorkspacePermissionId",
param("serviceAccountId").exists().isString().trim(),
param("serviceAccountWorkspacePermissionId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
requireServiceAccountWorkspacePermissionAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
serviceAccountsController.deleteServiceAccountWorkspacePermission
);
// router.get(
// "/:serviceAccountId/permissions/workspace",
// param("serviceAccountId").exists().isString().trim(),
// validateRequest,
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// serviceAccountsController.getServiceAccountWorkspacePermissions
// );
router.get(
"/:serviceAccountId/keys",
query("workspaceId").optional().isString(),
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT],
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED],
}),
serviceAccountsController.getServiceAccountKeys
);
// router.post(
// "/:serviceAccountId/permissions/workspace",
// param("serviceAccountId").exists().isString().trim(),
// body("workspaceId").exists().isString().notEmpty(),
// body("environment").exists().isString().notEmpty(),
// body("read").isBoolean().optional(),
// body("write").isBoolean().optional(),
// body("encryptedKey").exists().isString().notEmpty(),
// body("nonce").exists().isString().notEmpty(),
// validateRequest,
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// requireWorkspaceAuth({
// acceptedRoles: [ADMIN, MEMBER],
// locationWorkspaceId: "body",
// }),
// serviceAccountsController.addServiceAccountWorkspacePermission
// );
// router.delete(
// "/:serviceAccountId/permissions/workspace/:serviceAccountWorkspacePermissionId",
// param("serviceAccountId").exists().isString().trim(),
// param("serviceAccountWorkspacePermissionId").exists().isString().trim(),
// validateRequest,
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// requireServiceAccountWorkspacePermissionAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// serviceAccountsController.deleteServiceAccountWorkspacePermission
// );
// router.get(
// "/:serviceAccountId/keys",
// query("workspaceId").optional().isString(),
// requireAuth({
// acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT],
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED],
// }),
// serviceAccountsController.getServiceAccountKeys
// );
export default router;

@ -9,9 +9,7 @@ import {
import { body, param } from "express-validator";
import {
ADMIN,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
AuthMode,
MEMBER,
PERMISSION_WRITE_SECRETS
} from "../../variables";
@ -20,7 +18,7 @@ import { serviceTokenDataController } from "../../controllers/v2";
router.get(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_SERVICE_TOKEN]
acceptedAuthModes: [AuthMode.SERVICE_TOKEN]
}),
serviceTokenDataController.getServiceTokenData
);
@ -28,7 +26,7 @@ router.get(
router.post(
"/",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT]
acceptedAuthModes: [AuthMode.JWT]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -63,7 +61,7 @@ router.post(
router.delete(
"/:serviceTokenDataId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT]
acceptedAuthModes: [AuthMode.JWT]
}),
requireServiceTokenDataAuth({
acceptedRoles: [ADMIN, MEMBER]

@ -6,7 +6,7 @@ import { signupController } from "../../controllers/v2";
import { authLimiter } from "../../helpers/rateLimiter";
router.post(
"/complete-account/signup",
"/complete-account/signup", // TODO endpoint: deprecate (moved to v3/signup/complete/account-signup)
authLimiter,
requireSignupAuth,
body("email").exists().isString().trim().notEmpty().isEmail(),
@ -27,7 +27,7 @@ router.post(
);
router.post(
"/complete-account/invite",
"/complete-account/invite", // TODO: consider moving to v3/users/new/complete-account/invite
authLimiter,
requireSignupAuth,
body("email").exists().isString().trim().notEmpty().isEmail(),

@ -9,14 +9,14 @@ import {
} from "../../middleware";
import {
ADMIN,
AUTH_MODE_JWT,
MEMBER,
AuthMode,
MEMBER
} from "../../variables";
router.get(
"/:workspaceId/tags",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [MEMBER, ADMIN],
@ -30,7 +30,7 @@ router.get(
router.delete(
"/tags/:tagId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
param("tagId").exists().trim(),
validateRequest,
@ -40,7 +40,7 @@ router.delete(
router.post(
"/:workspaceId/tags",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [MEMBER, ADMIN],

@ -6,18 +6,15 @@ import {
} from "../../middleware";
import { body, param } from "express-validator";
import { usersController } from "../../controllers/v2";
import { AuthMode } from "../../variables";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
} from "../../variables";
import {
AuthProvider
AuthMethod
} from "../../models";
router.get(
"/me",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
usersController.getMe
);
@ -25,7 +22,7 @@ router.get(
router.patch(
"/me/mfa",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
body("isMfaEnabled").exists().isBoolean(),
validateRequest,
@ -35,7 +32,7 @@ router.patch(
router.patch(
"/me/name",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
body("firstName").exists().isString(),
body("lastName").isString(),
@ -43,24 +40,28 @@ router.patch(
usersController.updateName
);
router.patch(
"/me/auth-provider",
router.put(
"/me/auth-methods",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
body("authMethods").exists().isArray({
min: 1,
}).custom((authMethods: AuthMethod[]) => {
return authMethods.every(provider => [
AuthMethod.EMAIL,
AuthMethod.GOOGLE,
AuthMethod.GITHUB
].includes(provider))
}),
body("authProvider").exists().isString().isIn([
AuthProvider.EMAIL,
AuthProvider.GOOGLE,
AuthProvider.GITHUB
]),
validateRequest,
usersController.updateAuthProvider
usersController.updateAuthMethods,
);
router.get(
"/me/organizations",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
usersController.getMyOrganizations
);
@ -68,7 +69,7 @@ router.get(
router.get(
"/me/api-keys",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
usersController.getMyAPIKeys
);
@ -76,7 +77,7 @@ router.get(
router.post(
"/me/api-keys",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
body("name").exists().isString().trim(),
body("expiresIn").isNumeric(),
@ -87,7 +88,7 @@ router.post(
router.delete(
"/me/api-keys/:apiKeyDataId",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
param("apiKeyDataId").exists().trim(),
validateRequest,
@ -97,7 +98,7 @@ router.delete(
router.get(
"/me/sessions",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
usersController.getMySessions
);
@ -105,7 +106,7 @@ router.get(
router.delete(
"/me/sessions",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
usersController.deleteMySessions
);

@ -9,17 +9,15 @@ import {
} from "../../middleware";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_TOKEN,
MEMBER,
AuthMode,
MEMBER
} from "../../variables";
import { workspaceController } from "../../controllers/v2";
router.post(
router.post( // TODO endpoint: deprecate (moved to POST v3/secrets)
"/:workspaceId/secrets",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -34,10 +32,10 @@ router.post(
workspaceController.pushWorkspaceSecrets
);
router.get(
router.get( // TODO endpoint: deprecate (moved to GET v3/secrets)
"/:workspaceId/secrets",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_TOKEN],
acceptedAuthModes: [AuthMode.JWT, AuthMode.SERVICE_TOKEN],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -50,10 +48,10 @@ router.get(
workspaceController.pullSecrets
);
router.get(
router.get( // TODO endpoint: consider moving to v3/users/me/workspaces/:workspaceId/key
"/:workspaceId/encrypted-key",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -67,7 +65,7 @@ router.get(
router.get(
"/:workspaceId/service-token-data",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -83,7 +81,7 @@ router.get( // new - TODO: rewire dashboard to this route
param("workspaceId").exists().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
@ -99,7 +97,7 @@ router.patch( // TODO - rewire dashboard to this route
body("role").exists().isString().trim().isIn([ADMIN, MEMBER]),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@ -118,7 +116,7 @@ router.delete( // TODO - rewire dashboard to this route
param("membershipId").exists().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@ -134,7 +132,7 @@ router.delete( // TODO - rewire dashboard to this route
router.patch(
"/:workspaceId/auto-capitalization",
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT]
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],

@ -5,10 +5,7 @@ import { body, param, query } from "express-validator";
import { secretsController } from "../../controllers/v3";
import {
ADMIN,
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
AuthMode,
MEMBER,
PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS,
@ -25,10 +22,9 @@ router.get(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
secretsController.getSecretsRaw
@ -44,10 +40,9 @@ router.get(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@ -73,10 +68,9 @@ router.post(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@ -102,10 +96,9 @@ router.patch(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@ -130,10 +123,9 @@ router.delete(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@ -156,10 +148,9 @@ router.get(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@ -189,13 +180,14 @@ router.post(
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({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@ -220,10 +212,9 @@ router.get(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@ -250,10 +241,9 @@ router.patch(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({
@ -278,10 +268,9 @@ router.delete(
validateRequest,
requireAuth({
acceptedAuthModes: [
AUTH_MODE_JWT,
AUTH_MODE_API_KEY,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_SERVICE_ACCOUNT
AuthMode.JWT,
AuthMode.API_KEY,
AuthMode.SERVICE_TOKEN
]
}),
requireWorkspaceAuth({

@ -6,7 +6,7 @@ import { authLimiter } from "../../helpers/rateLimiter";
import { validateRequest } from "../../middleware";
router.post(
"/complete-account/signup",
"/complete-account/signup", // TODO: consider moving endpoint to v3/users/new/complete-account/signup
authLimiter,
body("email").exists().isString().trim().notEmpty().isEmail(),
body("firstName").exists().isString().trim().notEmpty(),

@ -8,7 +8,7 @@ import {
import { workspacesController } from "../../controllers/v3";
import {
ADMIN,
AUTH_MODE_JWT,
AuthMode
} from "../../variables";
import { body, param } from "express-validator";
@ -19,7 +19,7 @@ router.get(
param("workspaceId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@ -33,7 +33,7 @@ router.get( // allow admins to get all workspace secrets (part of blind indices
param("workspaceId").exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],
@ -65,7 +65,7 @@ router.post( // allow admins to name all workspace secrets (part of blind indice
.withMessage("secretId must be a string"),
validateRequest,
requireAuth({
acceptedAuthModes: [AUTH_MODE_JWT],
acceptedAuthModes: [AuthMode.JWT],
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN],

@ -1,6 +1,7 @@
import { nanoid } from "nanoid";
import { Types } from "mongoose";
import Folder, { TFolderSchema } from "../models/folder";
import path from "path";
type TAppendFolderDTO = {
folderName: string;
@ -58,7 +59,7 @@ const appendChild = (folders: TFolderSchema, folderName: string) => {
id,
name: folderName,
children: [],
version: 1,
version: 1
});
return { id, name: folderName };
};
@ -108,10 +109,7 @@ export const deleteFolderById = (folders: TFolderSchema, folderId: string) => {
};
// bfs but return parent of the folderID
export const getParentFromFolderId = (
folders: TFolderSchema,
folderId: string
) => {
export const getParentFromFolderId = (folders: TFolderSchema, folderId: string) => {
const queue = [folders];
while (queue.length) {
const folder = queue.pop() as TFolderSchema;
@ -140,10 +138,7 @@ export const getAllFolderIds = (folders: TFolderSchema) => {
// We then record the number of childs of each root node
// When we reach leaf node or when all childs of a root node are visited
// We remove it from path recorded by using the total child record
export const searchByFolderIdWithDir = (
folders: TFolderSchema,
folderId: string
) => {
export const searchByFolderIdWithDir = (folders: TFolderSchema, folderId: string) => {
const stack = [folders];
const dir: Array<{ id: string; name: string }> = [];
const hits: Record<string, number> = {};
@ -172,6 +167,20 @@ export const searchByFolderIdWithDir = (
return;
};
// used for get folder path from id
export const getFolderWithPathFromId = (folders: TFolderSchema, parentFolderId: string) => {
const search = searchByFolderIdWithDir(folders, parentFolderId);
if (!search) {
throw { message: "Folder permission denied" };
}
const { folder, dir } = search;
const folderPath = path.join(
"/",
...dir.filter(({ name }) => name !== "root").map(({ name }) => name)
);
return { folder, folderPath, dir };
};
// to get folder of a path given
// Like /frontend/folder#1
export const getFolderByPath = (folders: TFolderSchema, searchPath: string) => {
@ -201,7 +210,7 @@ export const getFolderIdFromServiceToken = async (
) => {
const folders = await Folder.findOne({
workspace: workspaceId,
environment,
environment
});
if (!folders) {

@ -1,8 +1,6 @@
import { Types } from "mongoose";
import {
AuthData,
AuthData
} from "../../interfaces/middleware";
declare module "express" {

@ -30,6 +30,7 @@ export interface BatchSecretRequest {
export interface BatchSecret {
_id: string;
type: "shared" | "personal";
secretName: string;
secretBlindIndex: string;
secretKeyCiphertext: string;
secretKeyIV: string;

@ -3,7 +3,7 @@ import passport from "passport";
import { Types } from "mongoose";
import { AuthData } from "../interfaces/middleware";
import {
AuthProvider,
AuthMethod,
MembershipOrg,
Organization,
ServiceAccount,
@ -97,20 +97,20 @@ const initializePassport = async () => {
email
}).select("+publicKey");
if (user && user.authProvider !== AuthProvider.GOOGLE) {
done(InternalServerError());
}
if (!user) {
user = await new User({
email,
authProvider: AuthProvider.GOOGLE,
authId: profile.id,
authMethods: [AuthMethod.GOOGLE],
firstName: profile.name.givenName,
lastName: profile.name.familyName
}).save();
}
let isLinkingRequired = false;
if (!user.authMethods.includes(AuthMethod.GOOGLE)) {
isLinkingRequired = true;
}
const isUserCompleted = !!user.publicKey;
const providerAuthToken = createToken({
payload: {
@ -118,8 +118,9 @@ const initializePassport = async () => {
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
authProvider: user.authProvider,
authMethod: AuthMethod.GOOGLE,
isUserCompleted,
isLinkingRequired,
...(req.query.state ? {
callbackPort: req.query.state as string
} : {})
@ -150,20 +151,20 @@ const initializePassport = async () => {
let user = await User.findOne({
email
}).select("+publicKey");
if (user && user.authProvider !== AuthProvider.GITHUB) {
done(InternalServerError());
}
if (!user) {
user = await new User({
email: email,
authProvider: AuthProvider.GITHUB,
authId: profile.id,
authMethods: [AuthMethod.GITHUB],
firstName: profile.displayName,
lastName: ""
}).save();
}
let isLinkingRequired = false;
if (!user.authMethods.includes(AuthMethod.GITHUB)) {
isLinkingRequired = true;
}
const isUserCompleted = !!user.publicKey;
const providerAuthToken = createToken({
@ -172,8 +173,9 @@ const initializePassport = async () => {
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
authProvider: user.authProvider,
authMethod: AuthMethod.GITHUB,
isUserCompleted,
isLinkingRequired,
...(req.query.state ? {
callbackPort: req.query.state as string
} : {})
@ -218,7 +220,7 @@ const initializePassport = async () => {
audience: await getSiteURL()
});
if (ssoConfig.authProvider === AuthProvider.JUMPCLOUD_SAML) {
if (ssoConfig.authProvider.toString() === AuthMethod.JUMPCLOUD_SAML.toString()) {
samlConfig.wantAuthnResponseSigned = false;
}
@ -243,11 +245,21 @@ const initializePassport = async () => {
}).select("+publicKey");
if (user) {
if (!user.authProvider || user.authProvider === AuthProvider.EMAIL || user.authProvider === AuthProvider.GOOGLE) {
// if user does not have SAML enabled then update
const hasSamlEnabled = user.authMethods
.some(
(authMethod: AuthMethod) => [
AuthMethod.OKTA_SAML,
AuthMethod.AZURE_SAML,
AuthMethod.JUMPCLOUD_SAML
].includes(authMethod)
);
if (!hasSamlEnabled) {
await User.findByIdAndUpdate(
user._id,
{
authProvider: req.ssoConfig.authProvider
authMethods: [req.ssoConfig.authProvider]
},
{
new: true
@ -279,7 +291,7 @@ const initializePassport = async () => {
} else {
user = await new User({
email,
authProvider: req.ssoConfig.authProvider,
authMethods: [req.ssoConfig.authProvider],
firstName,
lastName
}).save();
@ -301,7 +313,7 @@ const initializePassport = async () => {
firstName,
lastName,
organizationName: organization?.name,
authProvider: user.authProvider,
authMethod: req.ssoConfig.authProvider,
isUserCompleted,
...(req.body.RelayState ? {
callbackPort: req.body.RelayState as string

@ -1,15 +1,15 @@
const CLI_USER_AGENT_NAME = "cli"
const K8_OPERATOR_AGENT_NAME = "k8-operator"
export const getChannelFromUserAgent = function (userAgent: string | undefined) {
import { UserAgentType } from "../ee/models"
export const getUserAgentType = function (userAgent: string | undefined) {
if (userAgent == undefined) {
return "other"
} else if (userAgent == CLI_USER_AGENT_NAME) {
return "cli"
} else if (userAgent == K8_OPERATOR_AGENT_NAME) {
return "k8-operator"
return UserAgentType.OTHER;
} else if (userAgent == UserAgentType.CLI) {
return UserAgentType.CLI;
} else if (userAgent == UserAgentType.K8_OPERATOR) {
return UserAgentType.K8_OPERATOR;
} else if (userAgent.toLowerCase().includes("mozilla")) {
return "web"
return UserAgentType.WEB;
} else {
return "other"
return UserAgentType.OTHER;
}
}

@ -11,6 +11,7 @@ import {
TrustedIP
} from "../../ee/models";
import {
AuthMethod,
BackupPrivateKey,
Bot,
BotOrg,
@ -21,6 +22,7 @@ import {
Secret,
SecretBlindIndexData,
ServiceTokenData,
User,
Workspace
} from "../../models";
import { generateKeyPair } from "../../utils/crypto";
@ -631,3 +633,40 @@ export const backfillTrustedIps = async () => {
console.log("Backfill: Trusted IPs complete");
}
}
export const backfillUserAuthMethods = async () => {
await User.updateMany(
{
authProvider: {
$exists: false
},
authMethods: {
$exists: false
}
},
{
authMethods: [AuthMethod.EMAIL]
}
);
await User.updateMany(
{
authProvider: {
$exists: true
},
authMethods: {
$exists: false
}
},
[
{
$set: {
authMethods: ["$authProvider"]
}
},
{
$unset: ["authProvider", "authId"]
}
]
);
}

@ -16,7 +16,8 @@ import {
backfillSecretVersions,
backfillServiceToken,
backfillServiceTokenMultiScope,
backfillTrustedIps
backfillTrustedIps,
backfillUserAuthMethods
} from "./backfillData";
import {
reencryptBotOrgKeys,
@ -84,6 +85,7 @@ export const setup = async () => {
await backfillIntegration();
await backfillServiceTokenMultiScope();
await backfillTrustedIps();
await backfillUserAuthMethods();
// re-encrypt any data previously encrypted under server hex 128-bit ENCRYPTION_KEY
// to base64 256-bit ROOT_ENCRYPTION_KEY

@ -1,25 +1,15 @@
import { Types } from "mongoose";
import {
Bot,
IServiceAccount,
IServiceTokenData,
IUser,
ServiceAccount,
ServiceTokenData,
User,
} from "../models";
import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
import { validateUserClientForWorkspace } from "./user";
import {
BotNotFoundError,
UnauthorizedRequestError,
} from "../utils/errors";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../variables";
import { AuthData } from "../interfaces/middleware";
import { ActorType } from "../ee/models";
/**
* Validate authenticated clients for bot with id [botId] based
@ -34,65 +24,24 @@ export const validateClientForBot = async ({
botId,
acceptedRoles,
}: {
authData: {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
};
authData: AuthData;
botId: Types.ObjectId;
acceptedRoles: Array<"admin" | "member">;
}) => {
const bot = await Bot.findById(botId);
if (!bot) throw BotNotFoundError();
if (
authData.authMode === AUTH_MODE_JWT &&
authData.authPayload instanceof User
) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: bot.workspace,
acceptedRoles,
});
return bot;
}
if (
authData.authMode === AUTH_MODE_SERVICE_ACCOUNT &&
authData.authPayload instanceof ServiceAccount
) {
await validateServiceAccountClientForWorkspace({
serviceAccount: authData.authPayload,
workspaceId: bot.workspace,
});
return bot;
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForWorkspace({
user: authData.authPayload as IUser,
workspaceId: bot.workspace,
acceptedRoles,
});
return bot;
case ActorType.SERVICE:
throw UnauthorizedRequestError({
message: "Failed service token authorization for bot",
});
}
if (
authData.authMode === AUTH_MODE_SERVICE_TOKEN &&
authData.authPayload instanceof ServiceTokenData
) {
throw UnauthorizedRequestError({
message: "Failed service token authorization for bot",
});
}
if (
authData.authMode === AUTH_MODE_API_KEY &&
authData.authPayload instanceof User
) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: bot.workspace,
acceptedRoles,
});
return bot;
}
throw BotNotFoundError({
message: "Failed client authorization for bot",
});
};

@ -1,15 +1,9 @@
import { Types } from "mongoose";
import {
IServiceAccount,
IServiceTokenData,
IUser,
Integration,
IntegrationAuth,
ServiceAccount,
ServiceTokenData,
User,
} from "../models";
import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
import { validateUserClientForWorkspace } from "./user";
import { IntegrationService } from "../services";
import {
@ -17,12 +11,8 @@ import {
IntegrationNotFoundError,
UnauthorizedRequestError,
} from "../utils/errors";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../variables";
import { AuthData } from "../interfaces/middleware";
import { ActorType } from "../ee/models";
/**
* Validate authenticated clients for integration with id [integrationId] based
@ -39,10 +29,7 @@ export const validateClientForIntegration = async ({
integrationId,
acceptedRoles,
}: {
authData: {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
};
authData: AuthData;
integrationId: Types.ObjectId;
acceptedRoles: Array<"admin" | "member">;
}) => {
@ -61,43 +48,19 @@ export const validateClientForIntegration = async ({
const accessToken = (await IntegrationService.getIntegrationAuthAccess({
integrationAuthId: integrationAuth._id,
})).accessToken;
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: integration.workspace,
acceptedRoles,
});
return ({ integration, accessToken });
}
if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) {
await validateServiceAccountClientForWorkspace({
serviceAccount: authData.authPayload,
workspaceId: integration.workspace,
});
return ({ integration, accessToken });
}
if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) {
throw UnauthorizedRequestError({
message: "Failed service token authorization for integration",
});
}
if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: integration.workspace,
acceptedRoles,
});
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForWorkspace({
user: authData.authPayload as IUser,
workspaceId: integration.workspace,
acceptedRoles,
});
return ({ integration, accessToken });
return ({ integration, accessToken });
case ActorType.SERVICE:
throw UnauthorizedRequestError({
message: "Failed service token authorization for integration",
});
}
throw UnauthorizedRequestError({
message: "Failed client authorization for integration",
});
}

@ -1,27 +1,17 @@
import { Types } from "mongoose";
import {
IServiceAccount,
IServiceTokenData,
IUser,
IWorkspace,
IntegrationAuth,
ServiceAccount,
ServiceTokenData,
User,
} from "../models";
import {
AUTH_MODE_API_KEY,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
} from "../variables";
import {
IntegrationAuthNotFoundError,
UnauthorizedRequestError,
} from "../utils/errors";
import { IntegrationService } from "../services";
import { validateUserClientForWorkspace } from "./user";
import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
import { AuthData } from "../interfaces/middleware";
import { ActorType } from "../ee/models";
/**
* Validate authenticated clients for integration authorization with id [integrationAuthId] based
@ -38,10 +28,7 @@ import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
acceptedRoles,
attachAccessToken,
}: {
authData: {
authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData;
};
authData: AuthData;
integrationAuthId: Types.ObjectId;
acceptedRoles: Array<"admin" | "member">;
attachAccessToken?: boolean;
@ -66,44 +53,20 @@ import { validateServiceAccountClientForWorkspace } from "./serviceAccount";
accessId = access.accessId;
}
if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: integrationAuth.workspace._id,
acceptedRoles,
});
switch (authData.actor.type) {
case ActorType.USER:
await validateUserClientForWorkspace({
user: authData.authPayload as IUser,
workspaceId: integrationAuth.workspace._id,
acceptedRoles,
});
return ({ integrationAuth, accessToken, accessId });
return ({ integrationAuth, accessToken, accessId });
case ActorType.SERVICE:
throw UnauthorizedRequestError({
message: "Failed service token authorization for integration authorization",
});
}
if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) {
await validateServiceAccountClientForWorkspace({
serviceAccount: authData.authPayload,
workspaceId: integrationAuth.workspace._id,
});
return ({ integrationAuth, accessToken, accessId });
}
if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) {
throw UnauthorizedRequestError({
message: "Failed service token authorization for integration authorization",
});
}
if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) {
await validateUserClientForWorkspace({
user: authData.authPayload,
workspaceId: integrationAuth.workspace._id,
acceptedRoles,
});
return ({ integrationAuth, accessToken, accessId });
}
throw UnauthorizedRequestError({
message: "Failed client authorization for integration authorization",
});
}
export {

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

Loading…
Cancel
Save