improvement(audit-log): pass actor info through auth layer, remove redundant getSession calls

This commit is contained in:
waleed
2026-02-18 09:56:18 -08:00
parent a08b40838d
commit 35a69dce07
17 changed files with 89 additions and 89 deletions

View File

@@ -2,7 +2,6 @@ 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 {
@@ -199,12 +198,11 @@ 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,
actorName: auth.userName,
actorEmail: auth.userEmail,
action: AuditAction.DOCUMENT_UPDATED,
resourceType: AuditResourceType.DOCUMENT,
resourceId: documentId,
@@ -273,12 +271,11 @@ 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,
actorName: auth.userName,
actorEmail: auth.userEmail,
action: AuditAction.DOCUMENT_DELETED,
resourceType: AuditResourceType.DOCUMENT,
resourceId: documentId,

View File

@@ -209,8 +209,6 @@ 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)
@@ -250,8 +248,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,
actorName: auth.userName,
actorEmail: auth.userEmail,
action: AuditAction.DOCUMENT_UPLOADED,
resourceType: AuditResourceType.DOCUMENT,
resourceId: knowledgeBaseId,
@@ -311,8 +309,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,
actorName: auth.userName,
actorEmail: auth.userEmail,
action: AuditAction.DOCUMENT_UPLOADED,
resourceType: AuditResourceType.DOCUMENT,
resourceId: knowledgeBaseId,

View File

@@ -2,7 +2,6 @@ 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'
@@ -137,12 +136,11 @@ 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,
actorName: auth.userName,
actorEmail: auth.userEmail,
action: AuditAction.KNOWLEDGE_BASE_UPDATED,
resourceType: AuditResourceType.KNOWLEDGE_BASE,
resourceId: id,
@@ -213,12 +211,11 @@ 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,
actorName: auth.userName,
actorEmail: auth.userEmail,
action: AuditAction.KNOWLEDGE_BASE_DELETED,
resourceType: AuditResourceType.KNOWLEDGE_BASE,
resourceId: id,

View File

@@ -4,7 +4,6 @@ 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'
@@ -18,7 +17,11 @@ export const dynamic = 'force-dynamic'
* PATCH - Update an MCP server in the workspace (requires write or admin permission)
*/
export const PATCH = withMcpAuth<{ id: string }>('write')(
async (request: NextRequest, { userId, workspaceId, requestId }, { params }) => {
async (
request: NextRequest,
{ userId, userName, userEmail, workspaceId, requestId },
{ params }
) => {
const { id: serverId } = await params
try {
@@ -88,12 +91,11 @@ 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,
actorName: userName,
actorEmail: userEmail,
action: AuditAction.MCP_SERVER_UPDATED,
resourceType: AuditResourceType.MCP_SERVER,
resourceId: serverId,

View File

@@ -4,7 +4,6 @@ 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'
@@ -57,7 +56,7 @@ export const GET = withMcpAuth('read')(
* it will be updated instead of creating a duplicate.
*/
export const POST = withMcpAuth('write')(
async (request: NextRequest, { userId, workspaceId, requestId }) => {
async (request: NextRequest, { userId, userName, userEmail, workspaceId, requestId }) => {
try {
const body = getParsedBody(request) || (await request.json())
@@ -163,12 +162,11 @@ export const POST = withMcpAuth('write')(
// Silently fail
}
const session = await getSession()
recordAudit({
workspaceId,
actorId: userId,
actorName: session?.user?.name,
actorEmail: session?.user?.email,
actorName: userName,
actorEmail: userEmail,
action: AuditAction.MCP_SERVER_ADDED,
resourceType: AuditResourceType.MCP_SERVER,
resourceId: serverId,
@@ -194,7 +192,7 @@ export const POST = withMcpAuth('write')(
* DELETE - Delete an MCP server from the workspace (requires admin permission)
*/
export const DELETE = withMcpAuth('admin')(
async (request: NextRequest, { userId, workspaceId, requestId }) => {
async (request: NextRequest, { userId, userName, userEmail, workspaceId, requestId }) => {
try {
const { searchParams } = new URL(request.url)
const serverId = searchParams.get('serverId')
@@ -226,12 +224,11 @@ 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,
actorName: userName,
actorEmail: userEmail,
action: AuditAction.MCP_SERVER_REMOVED,
resourceType: AuditResourceType.MCP_SERVER,
resourceId: serverId!,

View File

@@ -4,7 +4,6 @@ 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'
@@ -73,7 +72,11 @@ export const GET = withMcpAuth<RouteParams>('read')(
* PATCH - Update a workflow MCP server
*/
export const PATCH = withMcpAuth<RouteParams>('write')(
async (request: NextRequest, { userId, workspaceId, requestId }, { params }) => {
async (
request: NextRequest,
{ userId, userName, userEmail, workspaceId, requestId },
{ params }
) => {
try {
const { id: serverId } = await params
const body = getParsedBody(request) || (await request.json())
@@ -114,12 +117,11 @@ 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,
actorName: userName,
actorEmail: userEmail,
action: AuditAction.MCP_SERVER_UPDATED,
resourceType: AuditResourceType.MCP_SERVER,
resourceId: serverId,
@@ -144,7 +146,11 @@ export const PATCH = withMcpAuth<RouteParams>('write')(
* DELETE - Delete a workflow MCP server and all its tools
*/
export const DELETE = withMcpAuth<RouteParams>('admin')(
async (request: NextRequest, { userId, workspaceId, requestId }, { params }) => {
async (
request: NextRequest,
{ userId, userName, userEmail, workspaceId, requestId },
{ params }
) => {
try {
const { id: serverId } = await params
@@ -165,12 +171,11 @@ 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,
actorName: userName,
actorEmail: userEmail,
action: AuditAction.MCP_SERVER_REMOVED,
resourceType: AuditResourceType.MCP_SERVER,
resourceId: serverId,

View File

@@ -4,7 +4,6 @@ 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'
@@ -67,7 +66,11 @@ export const GET = withMcpAuth<RouteParams>('read')(
* PATCH - Update a tool's configuration
*/
export const PATCH = withMcpAuth<RouteParams>('write')(
async (request: NextRequest, { userId, workspaceId, requestId }, { params }) => {
async (
request: NextRequest,
{ userId, userName, userEmail, workspaceId, requestId },
{ params }
) => {
try {
const { id: serverId, toolId } = await params
const body = getParsedBody(request) || (await request.json())
@@ -120,12 +123,11 @@ 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,
actorName: userName,
actorEmail: userEmail,
action: AuditAction.MCP_SERVER_UPDATED,
resourceType: AuditResourceType.MCP_SERVER,
resourceId: serverId,
@@ -150,7 +152,11 @@ export const PATCH = withMcpAuth<RouteParams>('write')(
* DELETE - Remove a tool from an MCP server
*/
export const DELETE = withMcpAuth<RouteParams>('write')(
async (request: NextRequest, { userId, workspaceId, requestId }, { params }) => {
async (
request: NextRequest,
{ userId, userName, userEmail, workspaceId, requestId },
{ params }
) => {
try {
const { id: serverId, toolId } = await params
@@ -181,12 +187,11 @@ 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,
actorName: userName,
actorEmail: userEmail,
action: AuditAction.MCP_SERVER_UPDATED,
resourceType: AuditResourceType.MCP_SERVER,
resourceId: serverId,

View File

@@ -4,7 +4,6 @@ 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'
@@ -78,7 +77,11 @@ export const GET = withMcpAuth<RouteParams>('read')(
* POST - Add a workflow as a tool to an MCP server
*/
export const POST = withMcpAuth<RouteParams>('write')(
async (request: NextRequest, { userId, workspaceId, requestId }, { params }) => {
async (
request: NextRequest,
{ userId, userName, userEmail, workspaceId, requestId },
{ params }
) => {
try {
const { id: serverId } = await params
const body = getParsedBody(request) || (await request.json())
@@ -199,12 +202,11 @@ 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,
actorName: userName,
actorEmail: userEmail,
action: AuditAction.MCP_SERVER_UPDATED,
resourceType: AuditResourceType.MCP_SERVER,
resourceId: serverId,

View File

@@ -4,7 +4,6 @@ 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'
@@ -87,7 +86,7 @@ export const GET = withMcpAuth('read')(
* POST - Create a new workflow MCP server
*/
export const POST = withMcpAuth('write')(
async (request: NextRequest, { userId, workspaceId, requestId }) => {
async (request: NextRequest, { userId, userName, userEmail, workspaceId, requestId }) => {
try {
const body = getParsedBody(request) || (await request.json())
@@ -190,12 +189,11 @@ 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,
actorName: userName,
actorEmail: userEmail,
action: AuditAction.MCP_SERVER_ADDED,
resourceType: AuditResourceType.MCP_SERVER,
resourceId: serverId,

View File

@@ -4,7 +4,6 @@ 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'
@@ -263,12 +262,11 @@ 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,
actorName: auth.userName,
actorEmail: auth.userEmail,
action: AuditAction.WEBHOOK_DELETED,
resourceType: AuditResourceType.WEBHOOK,
resourceId: id,

View File

@@ -146,7 +146,8 @@ export async function GET(request: NextRequest) {
// Create or Update a webhook
export async function POST(request: NextRequest) {
const requestId = generateRequestId()
const userId = (await getSession())?.user?.id
const session = await getSession()
const userId = session?.user?.id
if (!userId) {
logger.warn(`[${requestId}] Unauthorized webhook creation attempt`)
@@ -680,12 +681,11 @@ 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,
actorName: session?.user?.name,
actorEmail: session?.user?.email,
action: AuditAction.WEBHOOK_CREATED,
resourceType: AuditResourceType.WEBHOOK,
resourceId: savedWebhook.id,

View File

@@ -2,7 +2,6 @@ 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'
@@ -63,12 +62,11 @@ 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,
actorName: auth.userName,
actorEmail: auth.userEmail,
action: AuditAction.WORKFLOW_DUPLICATED,
resourceType: AuditResourceType.WORKFLOW,
resourceId: result.id,

View File

@@ -5,7 +5,6 @@ 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'
@@ -338,12 +337,11 @@ 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,
actorName: auth.userName,
actorEmail: auth.userEmail,
action: AuditAction.WORKFLOW_DELETED,
resourceType: AuditResourceType.WORKFLOW,
resourceId: workflowId,
@@ -425,12 +423,11 @@ 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,
actorName: auth.userName,
actorEmail: auth.userEmail,
action: AuditAction.WORKFLOW_UPDATED,
resourceType: AuditResourceType.WORKFLOW,
resourceId: workflowId,

View File

@@ -5,7 +5,6 @@ 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'
@@ -81,12 +80,11 @@ 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,
actorName: auth.userName,
actorEmail: auth.userEmail,
action: AuditAction.WORKFLOW_VARIABLES_UPDATED,
resourceType: AuditResourceType.WORKFLOW,
resourceId: workflowId,

View File

@@ -5,7 +5,6 @@ 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'
@@ -190,12 +189,11 @@ 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,
actorName: auth.userName,
actorEmail: auth.userEmail,
action: AuditAction.WORKFLOW_CREATED,
resourceType: AuditResourceType.WORKFLOW,
resourceId: workflowId,

View File

@@ -9,6 +9,8 @@ const logger = createLogger('HybridAuth')
export interface AuthResult {
success: boolean
userId?: string
userName?: string | null
userEmail?: string | null
authType?: 'session' | 'api_key' | 'internal_jwt'
apiKeyType?: 'personal' | 'workspace'
error?: string
@@ -142,6 +144,8 @@ export async function checkSessionOrInternalAuth(
return {
success: true,
userId: session.user.id,
userName: session.user.name,
userEmail: session.user.email,
authType: 'session',
}
}
@@ -189,6 +193,8 @@ export async function checkHybridAuth(
return {
success: true,
userId: session.user.id,
userName: session.user.name,
userEmail: session.user.email,
authType: 'session',
}
}

View File

@@ -11,6 +11,8 @@ export type McpPermissionLevel = 'read' | 'write' | 'admin'
export interface McpAuthContext {
userId: string
userName?: string | null
userEmail?: string | null
workspaceId: string
requestId: string
}
@@ -114,6 +116,8 @@ async function validateMcpAuth(
success: true,
context: {
userId: auth.userId,
userName: auth.userName,
userEmail: auth.userEmail,
workspaceId,
requestId,
},