mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
improvement(audit-log): add resource names and specific invitation actions
This commit is contained in:
@@ -14,6 +14,7 @@ async function getCredentialSetWithAccess(credentialSetId: string, userId: strin
|
||||
const [set] = await db
|
||||
.select({
|
||||
id: credentialSet.id,
|
||||
name: credentialSet.name,
|
||||
organizationId: credentialSet.organizationId,
|
||||
providerId: credentialSet.providerId,
|
||||
})
|
||||
@@ -186,7 +187,8 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i
|
||||
resourceId: id,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
description: `Removed member "${memberId}" from credential set "${id}"`,
|
||||
resourceName: result.set.name,
|
||||
description: `Removed member from credential set "${result.set.name}"`,
|
||||
request: req,
|
||||
})
|
||||
|
||||
|
||||
@@ -215,7 +215,8 @@ export async function DELETE(
|
||||
action: AuditAction.KNOWLEDGE_BASE_DELETED,
|
||||
resourceType: AuditResourceType.KNOWLEDGE_BASE,
|
||||
resourceId: id,
|
||||
description: `Deleted knowledge base "${id}"`,
|
||||
resourceName: accessCheck.knowledgeBase.name,
|
||||
description: `Deleted knowledge base "${accessCheck.knowledgeBase.name || id}"`,
|
||||
request: _request,
|
||||
})
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ export interface EmbeddingData {
|
||||
|
||||
export interface KnowledgeBaseAccessResult {
|
||||
hasAccess: true
|
||||
knowledgeBase: Pick<KnowledgeBaseData, 'id' | 'userId' | 'workspaceId'>
|
||||
knowledgeBase: Pick<KnowledgeBaseData, 'id' | 'userId' | 'workspaceId' | 'name'>
|
||||
}
|
||||
|
||||
export interface KnowledgeBaseAccessDenied {
|
||||
@@ -113,7 +113,7 @@ export type KnowledgeBaseAccessCheck = KnowledgeBaseAccessResult | KnowledgeBase
|
||||
export interface DocumentAccessResult {
|
||||
hasAccess: true
|
||||
document: DocumentData
|
||||
knowledgeBase: Pick<KnowledgeBaseData, 'id' | 'userId' | 'workspaceId'>
|
||||
knowledgeBase: Pick<KnowledgeBaseData, 'id' | 'userId' | 'workspaceId' | 'name'>
|
||||
}
|
||||
|
||||
export interface DocumentAccessDenied {
|
||||
@@ -128,7 +128,7 @@ export interface ChunkAccessResult {
|
||||
hasAccess: true
|
||||
chunk: EmbeddingData
|
||||
document: DocumentData
|
||||
knowledgeBase: Pick<KnowledgeBaseData, 'id' | 'userId' | 'workspaceId'>
|
||||
knowledgeBase: Pick<KnowledgeBaseData, 'id' | 'userId' | 'workspaceId' | 'name'>
|
||||
}
|
||||
|
||||
export interface ChunkAccessDenied {
|
||||
@@ -151,6 +151,7 @@ export async function checkKnowledgeBaseAccess(
|
||||
id: knowledgeBase.id,
|
||||
userId: knowledgeBase.userId,
|
||||
workspaceId: knowledgeBase.workspaceId,
|
||||
name: knowledgeBase.name,
|
||||
})
|
||||
.from(knowledgeBase)
|
||||
.where(and(eq(knowledgeBase.id, knowledgeBaseId), isNull(knowledgeBase.deletedAt)))
|
||||
@@ -193,6 +194,7 @@ export async function checkKnowledgeBaseWriteAccess(
|
||||
id: knowledgeBase.id,
|
||||
userId: knowledgeBase.userId,
|
||||
workspaceId: knowledgeBase.workspaceId,
|
||||
name: knowledgeBase.name,
|
||||
})
|
||||
.from(knowledgeBase)
|
||||
.where(and(eq(knowledgeBase.id, knowledgeBaseId), isNull(knowledgeBase.deletedAt)))
|
||||
|
||||
@@ -553,13 +553,16 @@ export async function PUT(
|
||||
email: orgInvitation.email,
|
||||
})
|
||||
|
||||
const auditActionMap = {
|
||||
accepted: AuditAction.ORG_INVITATION_ACCEPTED,
|
||||
rejected: AuditAction.ORG_INVITATION_REJECTED,
|
||||
cancelled: AuditAction.ORG_INVITATION_CANCELLED,
|
||||
} as const
|
||||
|
||||
recordAudit({
|
||||
workspaceId: null,
|
||||
actorId: session.user.id,
|
||||
action:
|
||||
status === 'accepted'
|
||||
? AuditAction.ORG_INVITATION_ACCEPTED
|
||||
: AuditAction.ORG_INVITATION_UPDATED,
|
||||
action: auditActionMap[status],
|
||||
resourceType: AuditResourceType.ORGANIZATION,
|
||||
resourceId: organizationId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
|
||||
@@ -14,6 +14,7 @@ async function getPermissionGroupWithAccess(groupId: string, userId: string) {
|
||||
const [group] = await db
|
||||
.select({
|
||||
id: permissionGroup.id,
|
||||
name: permissionGroup.name,
|
||||
organizationId: permissionGroup.organizationId,
|
||||
})
|
||||
.from(permissionGroup)
|
||||
@@ -158,9 +159,10 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
action: AuditAction.PERMISSION_GROUP_MEMBER_ADDED,
|
||||
resourceType: AuditResourceType.PERMISSION_GROUP,
|
||||
resourceId: id,
|
||||
resourceName: result.group.name,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
description: `Added member ${userId} to permission group`,
|
||||
description: `Added member ${userId} to permission group "${result.group.name}"`,
|
||||
metadata: { targetUserId: userId, permissionGroupId: id },
|
||||
request: req,
|
||||
})
|
||||
@@ -241,9 +243,10 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i
|
||||
action: AuditAction.PERMISSION_GROUP_MEMBER_REMOVED,
|
||||
resourceType: AuditResourceType.PERMISSION_GROUP,
|
||||
resourceId: id,
|
||||
resourceName: result.group.name,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
description: `Removed member ${memberToRemove.userId} from permission group`,
|
||||
description: `Removed member ${memberToRemove.userId} from permission group "${result.group.name}"`,
|
||||
metadata: { targetUserId: memberToRemove.userId, memberId, permissionGroupId: id },
|
||||
request: req,
|
||||
})
|
||||
|
||||
@@ -35,12 +35,14 @@ export async function DELETE(
|
||||
const result = await db
|
||||
.delete(apiKey)
|
||||
.where(and(eq(apiKey.id, keyId), eq(apiKey.userId, userId)))
|
||||
.returning({ id: apiKey.id })
|
||||
.returning({ id: apiKey.id, name: apiKey.name })
|
||||
|
||||
if (!result.length) {
|
||||
return NextResponse.json({ error: 'API key not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
const deletedKey = result[0]
|
||||
|
||||
recordAudit({
|
||||
workspaceId: null,
|
||||
actorId: userId,
|
||||
@@ -49,7 +51,8 @@ export async function DELETE(
|
||||
resourceId: keyId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
description: `Revoked personal API key: ${keyId}`,
|
||||
resourceName: deletedKey.name,
|
||||
description: `Revoked personal API key: ${deletedKey.name}`,
|
||||
request,
|
||||
})
|
||||
|
||||
|
||||
@@ -137,12 +137,14 @@ export async function DELETE(
|
||||
.where(
|
||||
and(eq(apiKey.workspaceId, workspaceId), eq(apiKey.id, keyId), eq(apiKey.type, 'workspace'))
|
||||
)
|
||||
.returning({ id: apiKey.id })
|
||||
.returning({ id: apiKey.id, name: apiKey.name })
|
||||
|
||||
if (deletedRows.length === 0) {
|
||||
return NextResponse.json({ error: 'API key not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
const deletedKey = deletedRows[0]
|
||||
|
||||
recordAudit({
|
||||
workspaceId,
|
||||
actorId: userId,
|
||||
@@ -151,7 +153,8 @@ export async function DELETE(
|
||||
resourceId: keyId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
description: `Revoked workspace API key: ${keyId}`,
|
||||
resourceName: deletedKey.name,
|
||||
description: `Revoked workspace API key: ${deletedKey.name}`,
|
||||
request,
|
||||
})
|
||||
|
||||
|
||||
@@ -258,6 +258,7 @@ export async function PUT(request: NextRequest, { params }: RouteParams) {
|
||||
action: AuditAction.NOTIFICATION_UPDATED,
|
||||
resourceType: AuditResourceType.NOTIFICATION,
|
||||
resourceId: notificationId,
|
||||
resourceName: subscription.notificationType,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
description: `Updated ${subscription.notificationType} notification subscription`,
|
||||
@@ -313,12 +314,17 @@ export async function DELETE(request: NextRequest, { params }: RouteParams) {
|
||||
eq(workspaceNotificationSubscription.workspaceId, workspaceId)
|
||||
)
|
||||
)
|
||||
.returning({ id: workspaceNotificationSubscription.id })
|
||||
.returning({
|
||||
id: workspaceNotificationSubscription.id,
|
||||
notificationType: workspaceNotificationSubscription.notificationType,
|
||||
})
|
||||
|
||||
if (deleted.length === 0) {
|
||||
return NextResponse.json({ error: 'Notification not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
const deletedSubscription = deleted[0]
|
||||
|
||||
logger.info('Deleted notification subscription', {
|
||||
workspaceId,
|
||||
subscriptionId: notificationId,
|
||||
@@ -332,7 +338,8 @@ export async function DELETE(request: NextRequest, { params }: RouteParams) {
|
||||
resourceId: notificationId,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
description: `Deleted notification subscription`,
|
||||
resourceName: deletedSubscription.notificationType,
|
||||
description: `Deleted ${deletedSubscription.notificationType} notification subscription`,
|
||||
request,
|
||||
})
|
||||
|
||||
|
||||
@@ -263,6 +263,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
action: AuditAction.NOTIFICATION_CREATED,
|
||||
resourceType: AuditResourceType.NOTIFICATION,
|
||||
resourceId: subscription.id,
|
||||
resourceName: data.notificationType,
|
||||
actorName: session.user.name ?? undefined,
|
||||
actorEmail: session.user.email ?? undefined,
|
||||
description: `Created ${data.notificationType} notification subscription`,
|
||||
|
||||
@@ -229,6 +229,13 @@ export async function DELETE(
|
||||
`Deleting workspace ${workspaceId} for user ${session.user.id}, deleteTemplates: ${deleteTemplates}`
|
||||
)
|
||||
|
||||
// Fetch workspace name before deletion for audit logging
|
||||
const [workspaceRecord] = await db
|
||||
.select({ name: workspace.name })
|
||||
.from(workspace)
|
||||
.where(eq(workspace.id, workspaceId))
|
||||
.limit(1)
|
||||
|
||||
// Delete workspace and all related data in a transaction
|
||||
await db.transaction(async (tx) => {
|
||||
// Get all workflows in this workspace before deletion
|
||||
@@ -290,7 +297,8 @@ export async function DELETE(
|
||||
action: AuditAction.WORKSPACE_DELETED,
|
||||
resourceType: AuditResourceType.WORKSPACE,
|
||||
resourceId: workspaceId,
|
||||
description: 'Deleted workspace',
|
||||
resourceName: workspaceRecord?.name,
|
||||
description: `Deleted workspace "${workspaceRecord?.name || workspaceId}"`,
|
||||
request,
|
||||
})
|
||||
|
||||
|
||||
@@ -89,7 +89,8 @@ export const AuditAction = {
|
||||
ORG_MEMBER_ROLE_CHANGED: 'org_member.role_changed',
|
||||
ORG_INVITATION_CREATED: 'org_invitation.created',
|
||||
ORG_INVITATION_ACCEPTED: 'org_invitation.accepted',
|
||||
ORG_INVITATION_UPDATED: 'org_invitation.updated',
|
||||
ORG_INVITATION_REJECTED: 'org_invitation.rejected',
|
||||
ORG_INVITATION_CANCELLED: 'org_invitation.cancelled',
|
||||
ORG_INVITATION_REVOKED: 'org_invitation.revoked',
|
||||
|
||||
// Permission Groups
|
||||
|
||||
@@ -62,7 +62,8 @@ export const auditMock = {
|
||||
ORG_MEMBER_ROLE_CHANGED: 'org_member.role_changed',
|
||||
ORG_INVITATION_CREATED: 'org_invitation.created',
|
||||
ORG_INVITATION_ACCEPTED: 'org_invitation.accepted',
|
||||
ORG_INVITATION_UPDATED: 'org_invitation.updated',
|
||||
ORG_INVITATION_REJECTED: 'org_invitation.rejected',
|
||||
ORG_INVITATION_CANCELLED: 'org_invitation.cancelled',
|
||||
ORG_INVITATION_REVOKED: 'org_invitation.revoked',
|
||||
PERMISSION_GROUP_CREATED: 'permission_group.created',
|
||||
PERMISSION_GROUP_UPDATED: 'permission_group.updated',
|
||||
|
||||
Reference in New Issue
Block a user