merge main

This commit is contained in:
Scott Wilson
2025-09-15 15:06:42 -07:00
605 changed files with 11025 additions and 4114 deletions
@@ -1,4 +1,5 @@
export enum LogProvider {
Azure = "azure",
Cribl = "cribl",
Custom = "custom",
Datadog = "datadog",
@@ -1,5 +1,6 @@
import { LogProvider } from "./audit-log-stream-enums";
import { TAuditLogStreamCredentials, TLogStreamFactory } from "./audit-log-stream-types";
import { AzureProviderFactory } from "./azure/azure-provider-factory";
import { CriblProviderFactory } from "./cribl/cribl-provider-factory";
import { CustomProviderFactory } from "./custom/custom-provider-factory";
import { DatadogProviderFactory } from "./datadog/datadog-provider-factory";
@@ -8,6 +9,7 @@ import { SplunkProviderFactory } from "./splunk/splunk-provider-factory";
type TLogStreamFactoryImplementation = TLogStreamFactory<TAuditLogStreamCredentials>;
export const LOG_STREAM_FACTORY_MAP: Record<LogProvider, TLogStreamFactoryImplementation> = {
[LogProvider.Azure]: AzureProviderFactory as TLogStreamFactoryImplementation,
[LogProvider.Datadog]: DatadogProviderFactory as TLogStreamFactoryImplementation,
[LogProvider.Splunk]: SplunkProviderFactory as TLogStreamFactoryImplementation,
[LogProvider.Custom]: CustomProviderFactory as TLogStreamFactoryImplementation,
@@ -3,6 +3,7 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { TAuditLogStream, TAuditLogStreamCredentials } from "./audit-log-stream-types";
import { getAzureProviderListItem } from "./azure/azure-provider-fns";
import { getCriblProviderListItem } from "./cribl/cribl-provider-fns";
import { getCustomProviderListItem } from "./custom/custom-provider-fns";
import { getDatadogProviderListItem } from "./datadog/datadog-provider-fns";
@@ -13,6 +14,7 @@ export const listProviderOptions = () => {
getDatadogProviderListItem(),
getSplunkProviderListItem(),
getCustomProviderListItem(),
getAzureProviderListItem(),
getCriblProviderListItem()
].sort((a, b) => a.name.localeCompare(b.name));
};
@@ -1,17 +1,19 @@
import { TAuditLogs } from "@app/db/schemas";
import { LogProvider } from "./audit-log-stream-enums";
import { TAzureProvider, TAzureProviderCredentials } from "./azure/azure-provider-types";
import { TCriblProvider, TCriblProviderCredentials } from "./cribl/cribl-provider-types";
import { TCustomProvider, TCustomProviderCredentials } from "./custom/custom-provider-types";
import { TDatadogProvider, TDatadogProviderCredentials } from "./datadog/datadog-provider-types";
import { TSplunkProvider, TSplunkProviderCredentials } from "./splunk/splunk-provider-types";
export type TAuditLogStream = TDatadogProvider | TSplunkProvider | TCustomProvider | TCriblProvider;
export type TAuditLogStream = TDatadogProvider | TSplunkProvider | TCustomProvider | TAzureProvider | TCriblProvider;
export type TAuditLogStreamCredentials =
| TDatadogProviderCredentials
| TSplunkProviderCredentials
| TCustomProviderCredentials
| TAzureProviderCredentials
| TCriblProviderCredentials;
export type TCreateAuditLogStreamDTO = {
@@ -0,0 +1,98 @@
import { RawAxiosRequestHeaders } from "axios";
import { request } from "@app/lib/config/request";
import { BadRequestError } from "@app/lib/errors";
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
import { AUDIT_LOG_STREAM_TIMEOUT } from "../../audit-log/audit-log-queue";
import { TLogStreamFactoryStreamLog, TLogStreamFactoryValidateCredentials } from "../audit-log-stream-types";
import { TAzureProviderCredentials } from "./azure-provider-types";
function createPayload(event: { createdAt?: Date | string } & Record<string, unknown>) {
return [
{
...event,
TimeGenerated: (event.createdAt ? new Date(event.createdAt) : new Date()).toISOString()
}
];
}
async function getAzureToken(tenantId: string, clientId: string, clientSecret: string) {
const { data } = await request.post<{ access_token: string }>(
`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
new URLSearchParams({
grant_type: "client_credentials",
client_id: clientId,
client_secret: clientSecret,
scope: "https://monitor.azure.com/.default"
}),
{
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
}
);
return data.access_token;
}
export const AzureProviderFactory = () => {
const validateCredentials: TLogStreamFactoryValidateCredentials<TAzureProviderCredentials> = async ({
credentials
}) => {
const { tenantId, clientId, clientSecret, dceUrl, dcrId, cltName } = credentials;
await blockLocalAndPrivateIpAddresses(dceUrl);
const token = await getAzureToken(tenantId, clientId, clientSecret);
const streamHeaders: RawAxiosRequestHeaders = {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
};
await request
.post(
`${dceUrl}/dataCollectionRules/${dcrId}/streams/Custom-${cltName}_CL?api-version=2023-01-01`,
createPayload({ ping: "ok" }),
{
headers: streamHeaders,
timeout: AUDIT_LOG_STREAM_TIMEOUT,
signal: AbortSignal.timeout(AUDIT_LOG_STREAM_TIMEOUT)
}
)
.catch((err) => {
throw new BadRequestError({ message: `Failed to connect with Azure: ${(err as Error)?.message}` });
});
return credentials;
};
const streamLog: TLogStreamFactoryStreamLog<TAzureProviderCredentials> = async ({ credentials, auditLog }) => {
const { tenantId, clientId, clientSecret, dceUrl, dcrId, cltName } = credentials;
await blockLocalAndPrivateIpAddresses(dceUrl);
const token = await getAzureToken(tenantId, clientId, clientSecret);
const streamHeaders: RawAxiosRequestHeaders = {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
};
await request.post(
`${dceUrl}/dataCollectionRules/${dcrId}/streams/Custom-${cltName}_CL?api-version=2023-01-01`,
createPayload(auditLog),
{
headers: streamHeaders,
timeout: AUDIT_LOG_STREAM_TIMEOUT,
signal: AbortSignal.timeout(AUDIT_LOG_STREAM_TIMEOUT)
}
);
};
return {
validateCredentials,
streamLog
};
};
@@ -0,0 +1,8 @@
import { LogProvider } from "../audit-log-stream-enums";
export const getAzureProviderListItem = () => {
return {
name: "Azure" as const,
provider: LogProvider.Azure as const
};
};
@@ -0,0 +1,52 @@
import RE2 from "re2";
import { z } from "zod";
import { LogProvider } from "../audit-log-stream-enums";
import { BaseProviderSchema } from "../audit-log-stream-schemas";
export const AzureProviderCredentialsSchema = z.object({
tenantId: z.string().trim().uuid(),
clientId: z.string().trim().uuid(),
clientSecret: z.string().trim().length(40),
// Data Collection Endpoint URL
dceUrl: z.string().trim().url().min(1).max(255),
// Data Collection Rule Immutable ID
dcrId: z
.string()
.trim()
.refine((val) => new RE2(/^dcr-[0-9a-f]{32}$/).test(val), "DCR ID must be in dcr-*** format"),
// Custom Log Table Name
cltName: z.string().trim().min(1).max(255)
});
const BaseAzureProviderSchema = BaseProviderSchema.extend({ provider: z.literal(LogProvider.Azure) });
export const AzureProviderSchema = BaseAzureProviderSchema.extend({
credentials: AzureProviderCredentialsSchema
});
export const SanitizedAzureProviderSchema = BaseAzureProviderSchema.extend({
credentials: AzureProviderCredentialsSchema.pick({
tenantId: true,
clientId: true,
dceUrl: true,
dcrId: true,
cltName: true
})
});
export const AzureProviderListItemSchema = z.object({
name: z.literal("Azure"),
provider: z.literal(LogProvider.Azure)
});
export const CreateAzureProviderLogStreamSchema = z.object({
credentials: AzureProviderCredentialsSchema
});
export const UpdateAzureProviderLogStreamSchema = z.object({
credentials: AzureProviderCredentialsSchema
});
@@ -0,0 +1,7 @@
import { z } from "zod";
import { AzureProviderCredentialsSchema, AzureProviderSchema } from "./azure-provider-schemas";
export type TAzureProvider = z.infer<typeof AzureProviderSchema>;
export type TAzureProviderCredentials = z.infer<typeof AzureProviderCredentialsSchema>;
@@ -146,7 +146,7 @@ export enum EventType {
MOVE_SECRETS = "move-secrets",
DELETE_SECRET = "delete-secret",
DELETE_SECRETS = "delete-secrets",
GET_WORKSPACE_KEY = "get-workspace-key",
GET_PROJECT_KEY = "get-project-key",
AUTHORIZE_INTEGRATION = "authorize-integration",
UPDATE_INTEGRATION_AUTH = "update-integration-auth",
UNAUTHORIZE_INTEGRATION = "unauthorize-integration",
@@ -249,9 +249,9 @@ export enum EventType {
UPDATE_ENVIRONMENT = "update-environment",
DELETE_ENVIRONMENT = "delete-environment",
GET_ENVIRONMENT = "get-environment",
ADD_WORKSPACE_MEMBER = "add-workspace-member",
ADD_BATCH_WORKSPACE_MEMBER = "add-workspace-members",
REMOVE_WORKSPACE_MEMBER = "remove-workspace-member",
ADD_PROJECT_MEMBER = "add-project-member",
ADD_BATCH_PROJECT_MEMBER = "add-project-members",
REMOVE_PROJECT_MEMBER = "remove-project-member",
CREATE_FOLDER = "create-folder",
UPDATE_FOLDER = "update-folder",
DELETE_FOLDER = "delete-folder",
@@ -264,8 +264,8 @@ export enum EventType {
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",
UPDATE_USER_PROJECT_ROLE = "update-user-project-role",
UPDATE_USER_PROJECT_DENIED_PERMISSIONS = "update-user-project-denied-permissions",
SECRET_APPROVAL_MERGED = "secret-approval-merged",
SECRET_APPROVAL_REQUEST = "secret-approval-request",
SECRET_APPROVAL_CLOSED = "secret-approval-closed",
@@ -666,8 +666,8 @@ interface DeleteSecretBatchEvent {
};
}
interface GetWorkspaceKeyEvent {
type: EventType.GET_WORKSPACE_KEY;
interface GetProjectKeyEvent {
type: EventType.GET_PROJECT_KEY;
metadata: {
keyId: string;
};
@@ -1559,24 +1559,24 @@ interface DeleteEnvironmentEvent {
};
}
interface AddWorkspaceMemberEvent {
type: EventType.ADD_WORKSPACE_MEMBER;
interface AddProjectMemberEvent {
type: EventType.ADD_PROJECT_MEMBER;
metadata: {
userId: string;
email: string;
};
}
interface AddBatchWorkspaceMemberEvent {
type: EventType.ADD_BATCH_WORKSPACE_MEMBER;
interface AddBatchProjectMemberEvent {
type: EventType.ADD_BATCH_PROJECT_MEMBER;
metadata: Array<{
userId: string;
email: string;
}>;
}
interface RemoveWorkspaceMemberEvent {
type: EventType.REMOVE_WORKSPACE_MEMBER;
interface RemoveProjectMemberEvent {
type: EventType.REMOVE_PROJECT_MEMBER;
metadata: {
userId: string;
email: string;
@@ -1715,7 +1715,7 @@ interface DeleteSecretImportEvent {
}
interface UpdateUserRole {
type: EventType.UPDATE_USER_WORKSPACE_ROLE;
type: EventType.UPDATE_USER_PROJECT_ROLE;
metadata: {
userId: string;
email: string;
@@ -1725,7 +1725,7 @@ interface UpdateUserRole {
}
interface UpdateUserDeniedPermissions {
type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS;
type: EventType.UPDATE_USER_PROJECT_DENIED_PERMISSIONS;
metadata: {
userId: string;
email: string;
@@ -3496,7 +3496,7 @@ export type Event =
| MoveSecretsEvent
| DeleteSecretEvent
| DeleteSecretBatchEvent
| GetWorkspaceKeyEvent
| GetProjectKeyEvent
| AuthorizeIntegrationEvent
| UpdateIntegrationAuthEvent
| UnauthorizeIntegrationEvent
@@ -3585,9 +3585,9 @@ export type Event =
| GetEnvironmentEvent
| UpdateEnvironmentEvent
| DeleteEnvironmentEvent
| AddWorkspaceMemberEvent
| AddBatchWorkspaceMemberEvent
| RemoveWorkspaceMemberEvent
| AddProjectMemberEvent
| AddBatchProjectMemberEvent
| RemoveProjectMemberEvent
| CreateFolderEvent
| UpdateFolderEvent
| DeleteFolderEvent
@@ -87,6 +87,7 @@ export enum OrgPermissionBillingActions {
export enum OrgPermissionSubjects {
Workspace = "workspace",
Project = "project",
Role = "role",
Member = "member",
Settings = "settings",
@@ -117,6 +118,7 @@ export type AppConnectionSubjectFields = {
export type OrgPermissionSet =
| [OrgPermissionActions.Create, OrgPermissionSubjects.Workspace]
| [OrgPermissionActions.Create, OrgPermissionSubjects.Project]
| [OrgPermissionActions, OrgPermissionSubjects.Role]
| [OrgPermissionActions, OrgPermissionSubjects.Member]
| [OrgPermissionActions, OrgPermissionSubjects.Settings]
@@ -166,6 +168,10 @@ export const OrgPermissionSchema = z.discriminatedUnion("subject", [
subject: z.literal(OrgPermissionSubjects.Workspace).describe("The entity this permission pertains to."),
action: CASL_ACTION_SCHEMA_ENUM([OrgPermissionActions.Create]).describe("Describe what action an entity can take.")
}),
z.object({
subject: z.literal(OrgPermissionSubjects.Project).describe("The entity this permission pertains to."),
action: CASL_ACTION_SCHEMA_ENUM([OrgPermissionActions.Create]).describe("Describe what action an entity can take.")
}),
z.object({
subject: z.literal(OrgPermissionSubjects.Role).describe("The entity this permission pertains to."),
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
@@ -280,6 +286,7 @@ const buildAdminPermission = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
// ws permissions
can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
can(OrgPermissionActions.Create, OrgPermissionSubjects.Project);
// role permission
can(OrgPermissionActions.Read, OrgPermissionSubjects.Role);
can(OrgPermissionActions.Create, OrgPermissionSubjects.Role);
@@ -413,6 +420,7 @@ const buildMemberPermission = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
can(OrgPermissionActions.Create, OrgPermissionSubjects.Project);
can(OrgPermissionActions.Read, OrgPermissionSubjects.Member);
can(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
can(OrgPermissionActions.Read, OrgPermissionSubjects.Role);
@@ -431,7 +431,7 @@ export const secretRotationQueueFactory = ({
numberOfSecrets: numberOfSecretsRotated,
environment: secretRotation.environment.slug,
secretPath: secretRotation.secretPath,
workspaceId: secretRotation.projectId
projectId: secretRotation.projectId
}
});