mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
improvement(audit-log): add actorName/actorEmail to all routes, add workflow.updated event
This commit is contained in:
@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import {
|
||||
@@ -198,9 +199,12 @@ export async function PUT(
|
||||
`[${requestId}] Document updated: ${documentId} in knowledge base ${knowledgeBaseId}`
|
||||
)
|
||||
|
||||
const auditSession = await getSession()
|
||||
recordAudit({
|
||||
workspaceId: accessCheck.knowledgeBase?.workspaceId ?? null,
|
||||
actorId: userId,
|
||||
actorName: auditSession?.user?.name,
|
||||
actorEmail: auditSession?.user?.email,
|
||||
action: AuditAction.DOCUMENT_UPDATED,
|
||||
resourceType: AuditResourceType.DOCUMENT,
|
||||
resourceId: documentId,
|
||||
@@ -269,9 +273,12 @@ export async function DELETE(
|
||||
`[${requestId}] Document deleted: ${documentId} from knowledge base ${knowledgeBaseId}`
|
||||
)
|
||||
|
||||
const auditSession = await getSession()
|
||||
recordAudit({
|
||||
workspaceId: accessCheck.knowledgeBase?.workspaceId ?? null,
|
||||
actorId: userId,
|
||||
actorName: auditSession?.user?.name,
|
||||
actorEmail: auditSession?.user?.email,
|
||||
action: AuditAction.DOCUMENT_DELETED,
|
||||
resourceType: AuditResourceType.DOCUMENT,
|
||||
resourceId: documentId,
|
||||
|
||||
@@ -209,6 +209,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const auditSession = await getSession()
|
||||
|
||||
if (body.bulk === true) {
|
||||
try {
|
||||
const validatedData = BulkCreateDocumentsSchema.parse(body)
|
||||
@@ -248,6 +250,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
recordAudit({
|
||||
workspaceId: accessCheck.knowledgeBase?.workspaceId ?? null,
|
||||
actorId: userId,
|
||||
actorName: auditSession?.user?.name,
|
||||
actorEmail: auditSession?.user?.email,
|
||||
action: AuditAction.DOCUMENT_UPLOADED,
|
||||
resourceType: AuditResourceType.DOCUMENT,
|
||||
resourceId: knowledgeBaseId,
|
||||
@@ -307,6 +311,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
recordAudit({
|
||||
workspaceId: accessCheck.knowledgeBase?.workspaceId ?? null,
|
||||
actorId: userId,
|
||||
actorName: auditSession?.user?.name,
|
||||
actorEmail: auditSession?.user?.email,
|
||||
action: AuditAction.DOCUMENT_UPLOADED,
|
||||
resourceType: AuditResourceType.DOCUMENT,
|
||||
resourceId: knowledgeBaseId,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { PlatformEvents } from '@/lib/core/telemetry'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
@@ -136,9 +137,12 @@ export async function PUT(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
|
||||
logger.info(`[${requestId}] Knowledge base updated: ${id} for user ${userId}`)
|
||||
|
||||
const auditSession = await getSession()
|
||||
recordAudit({
|
||||
workspaceId: accessCheck.knowledgeBase.workspaceId ?? null,
|
||||
actorId: userId,
|
||||
actorName: auditSession?.user?.name,
|
||||
actorEmail: auditSession?.user?.email,
|
||||
action: AuditAction.KNOWLEDGE_BASE_UPDATED,
|
||||
resourceType: AuditResourceType.KNOWLEDGE_BASE,
|
||||
resourceId: id,
|
||||
@@ -209,9 +213,12 @@ export async function DELETE(
|
||||
|
||||
logger.info(`[${requestId}] Knowledge base deleted: ${id} for user ${userId}`)
|
||||
|
||||
const auditSession = await getSession()
|
||||
recordAudit({
|
||||
workspaceId: accessCheck.knowledgeBase.workspaceId ?? null,
|
||||
actorId: userId,
|
||||
actorName: auditSession?.user?.name,
|
||||
actorEmail: auditSession?.user?.email,
|
||||
action: AuditAction.KNOWLEDGE_BASE_DELETED,
|
||||
resourceType: AuditResourceType.KNOWLEDGE_BASE,
|
||||
resourceId: id,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { McpDomainNotAllowedError, validateMcpDomain } from '@/lib/mcp/domain-check'
|
||||
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { mcpService } from '@/lib/mcp/service'
|
||||
@@ -87,9 +88,12 @@ export const PATCH = withMcpAuth<{ id: string }>('write')(
|
||||
|
||||
logger.info(`[${requestId}] Successfully updated MCP server: ${serverId}`)
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.MCP_SERVER_UPDATED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { McpDomainNotAllowedError, validateMcpDomain } from '@/lib/mcp/domain-check'
|
||||
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { mcpService } from '@/lib/mcp/service'
|
||||
@@ -162,9 +163,12 @@ export const POST = withMcpAuth('write')(
|
||||
// Silently fail
|
||||
}
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.MCP_SERVER_ADDED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
@@ -222,9 +226,12 @@ export const DELETE = withMcpAuth('admin')(
|
||||
|
||||
logger.info(`[${requestId}] Successfully deleted MCP server: ${serverId}`)
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.MCP_SERVER_REMOVED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId!,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { mcpPubSub } from '@/lib/mcp/pubsub'
|
||||
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
|
||||
@@ -113,9 +114,12 @@ export const PATCH = withMcpAuth<RouteParams>('write')(
|
||||
|
||||
logger.info(`[${requestId}] Successfully updated workflow MCP server: ${serverId}`)
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.MCP_SERVER_UPDATED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
@@ -161,9 +165,12 @@ export const DELETE = withMcpAuth<RouteParams>('admin')(
|
||||
|
||||
mcpPubSub?.publishWorkflowToolsChanged({ serverId, workspaceId })
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.MCP_SERVER_REMOVED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { mcpPubSub } from '@/lib/mcp/pubsub'
|
||||
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
|
||||
@@ -119,9 +120,12 @@ export const PATCH = withMcpAuth<RouteParams>('write')(
|
||||
|
||||
mcpPubSub?.publishWorkflowToolsChanged({ serverId, workspaceId })
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.MCP_SERVER_UPDATED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
@@ -177,9 +181,12 @@ export const DELETE = withMcpAuth<RouteParams>('write')(
|
||||
|
||||
mcpPubSub?.publishWorkflowToolsChanged({ serverId, workspaceId })
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.MCP_SERVER_UPDATED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { mcpPubSub } from '@/lib/mcp/pubsub'
|
||||
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
|
||||
@@ -198,9 +199,12 @@ export const POST = withMcpAuth<RouteParams>('write')(
|
||||
|
||||
mcpPubSub?.publishWorkflowToolsChanged({ serverId, workspaceId })
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.MCP_SERVER_UPDATED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { eq, inArray, sql } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { mcpPubSub } from '@/lib/mcp/pubsub'
|
||||
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
|
||||
@@ -189,9 +190,12 @@ export const POST = withMcpAuth('write')(
|
||||
`[${requestId}] Successfully created workflow MCP server: ${body.name} (ID: ${serverId})`
|
||||
)
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.MCP_SERVER_ADDED,
|
||||
resourceType: AuditResourceType.MCP_SERVER,
|
||||
resourceId: serverId,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { validateInteger } from '@/lib/core/security/input-validation'
|
||||
import { PlatformEvents } from '@/lib/core/telemetry'
|
||||
@@ -262,9 +263,12 @@ export async function DELETE(
|
||||
logger.info(`[${requestId}] Successfully deleted webhook: ${id}`)
|
||||
}
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId: webhookData.workflow.workspaceId || null,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.WEBHOOK_DELETED,
|
||||
resourceType: AuditResourceType.WEBHOOK,
|
||||
resourceId: id,
|
||||
|
||||
@@ -680,9 +680,12 @@ export async function POST(request: NextRequest) {
|
||||
// Telemetry should not fail the operation
|
||||
}
|
||||
|
||||
const auditSession = await getSession()
|
||||
recordAudit({
|
||||
workspaceId: workflowRecord.workspaceId || null,
|
||||
actorId: userId,
|
||||
actorName: auditSession?.user?.name,
|
||||
actorEmail: auditSession?.user?.email,
|
||||
action: AuditAction.WEBHOOK_CREATED,
|
||||
resourceType: AuditResourceType.WEBHOOK,
|
||||
resourceId: savedWebhook.id,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { PlatformEvents } from '@/lib/core/telemetry'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
@@ -62,9 +63,12 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
`[${requestId}] Successfully duplicated workflow ${sourceWorkflowId} to ${result.id} in ${elapsed}ms`
|
||||
)
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId: workspaceId || null,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.WORKFLOW_DUPLICATED,
|
||||
resourceType: AuditResourceType.WORKFLOW,
|
||||
resourceId: result.id,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { checkHybridAuth, checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { PlatformEvents } from '@/lib/core/telemetry'
|
||||
@@ -337,9 +338,12 @@ export async function DELETE(
|
||||
// Don't fail the deletion if Socket.IO notification fails
|
||||
}
|
||||
|
||||
const deleteSession = await getSession()
|
||||
recordAudit({
|
||||
workspaceId: workflowData.workspaceId || null,
|
||||
actorId: userId,
|
||||
actorName: deleteSession?.user?.name,
|
||||
actorEmail: deleteSession?.user?.email,
|
||||
action: AuditAction.WORKFLOW_DELETED,
|
||||
resourceType: AuditResourceType.WORKFLOW,
|
||||
resourceId: workflowId,
|
||||
@@ -421,6 +425,21 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
||||
updates: updateData,
|
||||
})
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId: workflowData.workspaceId || null,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.WORKFLOW_UPDATED,
|
||||
resourceType: AuditResourceType.WORKFLOW,
|
||||
resourceId: workflowId,
|
||||
resourceName: updatedWorkflow?.name ?? workflowData.name,
|
||||
description: `Updated workflow "${updatedWorkflow?.name ?? workflowData.name}"`,
|
||||
metadata: updates,
|
||||
request,
|
||||
})
|
||||
|
||||
return NextResponse.json({ workflow: updatedWorkflow }, { status: 200 })
|
||||
} catch (error: any) {
|
||||
const elapsed = Date.now() - startTime
|
||||
|
||||
@@ -5,6 +5,7 @@ import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
|
||||
@@ -80,9 +81,12 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
})
|
||||
.where(eq(workflow.id, workflowId))
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId: workflowData.workspaceId ?? null,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.WORKFLOW_VARIABLES_UPDATED,
|
||||
resourceType: AuditResourceType.WORKFLOW,
|
||||
resourceId: workflowId,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { and, asc, eq, inArray, isNull, min } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { getUserEntityPermissions, workspaceExists } from '@/lib/workspaces/permissions/utils'
|
||||
@@ -189,9 +190,12 @@ export async function POST(req: NextRequest) {
|
||||
|
||||
logger.info(`[${requestId}] Successfully created empty workflow ${workflowId}`)
|
||||
|
||||
const session = await getSession()
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
actorName: session?.user?.name,
|
||||
actorEmail: session?.user?.email,
|
||||
action: AuditAction.WORKFLOW_CREATED,
|
||||
resourceType: AuditResourceType.WORKFLOW,
|
||||
resourceId: workflowId,
|
||||
|
||||
@@ -109,6 +109,7 @@ export const AuditAction = {
|
||||
|
||||
// Workflows
|
||||
WORKFLOW_CREATED: 'workflow.created',
|
||||
WORKFLOW_UPDATED: 'workflow.updated',
|
||||
WORKFLOW_DELETED: 'workflow.deleted',
|
||||
WORKFLOW_DEPLOYED: 'workflow.deployed',
|
||||
WORKFLOW_UNDEPLOYED: 'workflow.undeployed',
|
||||
|
||||
@@ -74,6 +74,7 @@ export const auditMock = {
|
||||
WEBHOOK_CREATED: 'webhook.created',
|
||||
WEBHOOK_DELETED: 'webhook.deleted',
|
||||
WORKFLOW_CREATED: 'workflow.created',
|
||||
WORKFLOW_UPDATED: 'workflow.updated',
|
||||
WORKFLOW_DELETED: 'workflow.deleted',
|
||||
WORKFLOW_DEPLOYED: 'workflow.deployed',
|
||||
WORKFLOW_UNDEPLOYED: 'workflow.undeployed',
|
||||
|
||||
Reference in New Issue
Block a user