/** * Система аудита доступа к коммерческим данным * * Логирует все обращения к чувствительной коммерческой информации, * отслеживает подозрительную активность и генерирует алерты */ import { PrismaClient } from '@prisma/client' import { SecurityLogger } from '../../lib/security-logger' import { CommercialAccessType, ResourceType, SecurityAlert, AuditParams, AlertThresholds } from './types' /** * Статистика активности пользователя */ interface UserActivityStats { userId: string organizationType: string action: CommercialAccessType count: number timeframe: string lastAccess: Date } export class CommercialDataAudit { /** * Пороговые значения для различных типов доступа */ private static readonly ALERT_THRESHOLDS: AlertThresholds = { VIEW_PRICE: { perHour: 100, // Максимум 100 просмотров цен в час perDay: 500, // Максимум 500 просмотров цен в день }, VIEW_RECIPE: { perHour: 50, // Максимум 50 просмотров рецептур в час perDay: 200, // Максимум 200 просмотров рецептур в день }, VIEW_CONTACTS: { perHour: 30, // Максимум 30 просмотров контактов в час perDay: 100, // Максимум 100 просмотров контактов в день }, VIEW_MARGINS: { perHour: 20, // Максимум 20 просмотров маржинальности в час perDay: 80, // Максимум 80 просмотров маржинальности в день }, BULK_EXPORT: { perHour: 5, // Максимум 5 экспортов в час perDay: 20, // Максимум 20 экспортов в день }, } /** * Логирует доступ к коммерческим данным */ static async logAccess(prisma: PrismaClient, params: AuditParams): Promise { try { // Создаем запись в журнале аудита await prisma.auditLog.create({ data: { userId: params.userId, organizationType: params.organizationType, action: `DATA_ACCESS:${params.action}`, resourceType: params.resourceType, resourceId: params.resourceId || null, metadata: params.metadata || {}, ipAddress: params.ipAddress, userAgent: params.userAgent, }, }) // Логируем через SecurityLogger SecurityLogger.logDataAccess({ userId: params.userId, organizationType: params.organizationType, action: params.action, resource: params.resourceType, resourceId: params.resourceId, filtered: false, ipAddress: params.ipAddress, userAgent: params.userAgent, }) // Проверяем на подозрительную активность await this.checkSuspiciousActivity(prisma, params) } catch (error) { SecurityLogger.logSecurityError(error as Error, { operation: 'logAccess', params, }) } } /** * Логирует попытку несанкционированного доступа */ static async logUnauthorizedAccess( prisma: PrismaClient, params: { userId: string organizationType: string resourceType: ResourceType resourceId: string reason: string ipAddress?: string userAgent?: string }, ): Promise { try { // Записываем в журнал аудита await prisma.auditLog.create({ data: { userId: params.userId, organizationType: params.organizationType, action: 'UNAUTHORIZED_ACCESS_ATTEMPT', resourceType: params.resourceType, resourceId: params.resourceId, metadata: { reason: params.reason, blocked: true, }, ipAddress: params.ipAddress, userAgent: params.userAgent, }, }) // Генерируем алерт безопасности const alert: SecurityAlert = { id: `alert-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, type: 'UNAUTHORIZED_ATTEMPT', severity: 'HIGH', userId: params.userId, message: `Unauthorized access attempt to ${params.resourceType} ${params.resourceId}`, metadata: { resourceType: params.resourceType, resourceId: params.resourceId, reason: params.reason, organizationType: params.organizationType, ipAddress: params.ipAddress, userAgent: params.userAgent, }, timestamp: new Date(), resolved: false, } await this.processSecurityAlert(prisma, alert) SecurityLogger.logAccessAttempt({ userId: params.userId, organizationType: params.organizationType, resource: params.resourceType, resourceId: params.resourceId, success: false, reason: params.reason, ipAddress: params.ipAddress, userAgent: params.userAgent, }) } catch (error) { SecurityLogger.logSecurityError(error as Error, { operation: 'logUnauthorizedAccess', params, }) } } /** * Проверяет подозрительную активность пользователя */ private static async checkSuspiciousActivity(prisma: PrismaClient, params: AuditParams): Promise { const threshold = this.ALERT_THRESHOLDS[params.action] if (!threshold) return try { // Считаем активность за последний час const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000) const hourlyCount = await this.getActivityCount(prisma, params.userId, params.action, oneHourAgo) // Проверяем превышение почасового лимита if (hourlyCount > threshold.perHour) { await this.sendExcessiveAccessAlert(prisma, { userId: params.userId, organizationType: params.organizationType, action: params.action, count: hourlyCount, threshold: threshold.perHour, timeframe: '1 hour', severity: 'HIGH', }) } // Считаем активность за последние 24 часа const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000) const dailyCount = await this.getActivityCount(prisma, params.userId, params.action, oneDayAgo) // Проверяем превышение дневного лимита if (dailyCount > threshold.perDay) { await this.sendExcessiveAccessAlert(prisma, { userId: params.userId, organizationType: params.organizationType, action: params.action, count: dailyCount, threshold: threshold.perDay, timeframe: '24 hours', severity: 'MEDIUM', }) } } catch (error) { SecurityLogger.logSecurityError(error as Error, { operation: 'checkSuspiciousActivity', userId: params.userId, action: params.action, }) } } /** * Получает количество действий пользователя за период */ private static async getActivityCount( prisma: PrismaClient, userId: string, action: CommercialAccessType, since: Date, ): Promise { return await prisma.auditLog.count({ where: { userId, action: { contains: action }, timestamp: { gte: since }, }, }) } /** * Отправляет алерт о чрезмерной активности */ private static async sendExcessiveAccessAlert( prisma: PrismaClient, params: { userId: string organizationType: string action: CommercialAccessType count: number threshold: number timeframe: string severity: 'MEDIUM' | 'HIGH' }, ): Promise { const alert: SecurityAlert = { id: `alert-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, type: 'EXCESSIVE_ACCESS', severity: params.severity, userId: params.userId, message: `Excessive ${params.action} activity: ${params.count} actions in ` + `${params.timeframe} (threshold: ${params.threshold})`, metadata: { action: params.action, count: params.count, threshold: params.threshold, timeframe: params.timeframe, organizationType: params.organizationType, }, timestamp: new Date(), resolved: false, } await this.processSecurityAlert(prisma, alert) SecurityLogger.logSuspiciousActivity({ userId: params.userId, organizationType: params.organizationType, activity: params.action, count: params.count, timeframe: params.timeframe, threshold: params.threshold, }) } /** * Обрабатывает алерт безопасности */ private static async processSecurityAlert(prisma: PrismaClient, alert: SecurityAlert): Promise { try { // Сохраняем алерт в базу данных await prisma.securityAlert.create({ data: { id: alert.id, type: alert.type, severity: alert.severity, userId: alert.userId, message: alert.message, metadata: alert.metadata, timestamp: alert.timestamp, resolved: alert.resolved, }, }) // Логируем алерт SecurityLogger.logSecurityAlert(alert) // Для критичных алертов - немедленная отправка уведомлений if (alert.severity === 'HIGH' || alert.severity === 'CRITICAL') { await this.sendImmediateNotification(alert) } } catch (error) { SecurityLogger.logSecurityError(error as Error, { operation: 'processSecurityAlert', alertId: alert.id, }) } } /** * Отправляет немедленное уведомление о критичном алерте */ private static async sendImmediateNotification(alert: SecurityAlert): Promise { // TODO: Реализовать отправку уведомлений // - Email администраторам // - SMS для критичных алертов // - Slack/Teams уведомления // - Push уведомления в мобильное приложение console.error(`🚨 CRITICAL SECURITY ALERT: ${alert.message}`, { alertId: alert.id, userId: alert.userId, type: alert.type, severity: alert.severity, timestamp: alert.timestamp, }) } /** * Получает статистику активности для пользователя */ static async getUserActivityStats( prisma: PrismaClient, userId: string, period: '1h' | '24h' | '7d' = '24h', ): Promise { const periodMs = { '1h': 60 * 60 * 1000, '24h': 24 * 60 * 60 * 1000, '7d': 7 * 24 * 60 * 60 * 1000, } const since = new Date(Date.now() - periodMs[period]) try { const rawStats = await prisma.auditLog.groupBy({ by: ['userId', 'organizationType', 'action'], where: { userId, timestamp: { gte: since }, action: { startsWith: 'DATA_ACCESS:' }, }, _count: { action: true, }, _max: { timestamp: true, }, }) return rawStats.map((stat) => ({ userId: stat.userId, organizationType: (stat.organizationType as string) || 'UNKNOWN', action: stat.action.replace('DATA_ACCESS:', '') as CommercialAccessType, count: stat._count.action, timeframe: period, lastAccess: stat._max.timestamp || new Date(), })) } catch (error) { SecurityLogger.logSecurityError(error as Error, { operation: 'getUserActivityStats', userId, period, }) return [] } } /** * Получает активные алерты безопасности */ static async getActiveAlerts(prisma: PrismaClient, limit: number = 50): Promise { try { const alerts = await prisma.securityAlert.findMany({ where: { resolved: false, }, orderBy: { timestamp: 'desc', }, take: limit, }) return alerts.map((alert) => ({ id: alert.id, type: alert.type as SecurityAlert['type'], severity: alert.severity as SecurityAlert['severity'], userId: alert.userId, message: alert.message, metadata: alert.metadata as Record, timestamp: alert.timestamp, resolved: alert.resolved, })) } catch (error) { SecurityLogger.logSecurityError(error as Error, { operation: 'getActiveAlerts', }) return [] } } /** * Помечает алерт как разрешенный */ static async resolveAlert(prisma: PrismaClient, alertId: string, resolvedBy: string): Promise { try { await prisma.securityAlert.update({ where: { id: alertId }, data: { resolved: true, metadata: { resolvedBy, resolvedAt: new Date(), }, }, }) } catch (error) { SecurityLogger.logSecurityError(error as Error, { operation: 'resolveAlert', alertId, resolvedBy, }) } } }