Merge branch 'main' into add-bull-queue

This commit is contained in:
Maidul Islam
2023-08-19 13:46:26 -04:00
committed by GitHub
383 changed files with 7173 additions and 8536 deletions
@@ -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);
+47
View File
@@ -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"
}
+3
View File
@@ -0,0 +1,3 @@
export * from "./auditLog";
export * from "./enums";
export * from "./types";
+403
View File
@@ -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;
+1
View File
@@ -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";
+2 -1
View File
@@ -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,
+16 -16
View File
@@ -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],
+4 -3
View File
@@ -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],
+6 -6
View File
@@ -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(),
+2 -2
View File
@@ -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],
+4 -3
View File
@@ -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],
+2 -2
View File
@@ -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
);
+45 -10
View File
@@ -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;
}
}
+12 -9
View File
@@ -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()}-`);
}
}
+1 -3
View File
@@ -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;
+2
View File
@@ -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
}