[FEAT] Add EmailService, RateLimitService, ReportService + integrations
Services: - EmailService: CSIRT notifications (24h/72h/30d), training alerts, welcome email - RateLimitService: File-based rate limiting for auth and AI endpoints - ReportService: Executive HTML report, CSV exports (risks/incidents/controls/assets) Integrations: - AuthController: Rate limiting on login (5/min, 20/h) and register (3/10min) - IncidentController: Email notifications on CSIRT milestones - AuditController: Executive report and CSV export endpoints - Router: 429 rate limit error handling, new audit export routes Database: - Migration 002: email_log table for notification tracking Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
require_once __DIR__ . '/BaseController.php';
|
||||
require_once APP_PATH . '/services/AIService.php';
|
||||
require_once APP_PATH . '/services/EmailService.php';
|
||||
|
||||
class IncidentController extends BaseController
|
||||
{
|
||||
@@ -237,6 +238,9 @@ class IncidentController extends BaseController
|
||||
'created_by' => $this->getCurrentUserId(),
|
||||
]);
|
||||
|
||||
// Invia notifica email ai responsabili
|
||||
$this->notifyIncidentStakeholders($id, 'early_warning');
|
||||
|
||||
$this->logAudit('early_warning_sent', 'incident', $id);
|
||||
$this->jsonSuccess(null, 'Early warning registrato');
|
||||
}
|
||||
@@ -260,6 +264,9 @@ class IncidentController extends BaseController
|
||||
'created_by' => $this->getCurrentUserId(),
|
||||
]);
|
||||
|
||||
// Invia notifica email ai responsabili
|
||||
$this->notifyIncidentStakeholders($id, 'notification');
|
||||
|
||||
$this->logAudit('notification_sent', 'incident', $id);
|
||||
$this->jsonSuccess(null, 'Notifica CSIRT registrata');
|
||||
}
|
||||
@@ -283,10 +290,48 @@ class IncidentController extends BaseController
|
||||
'created_by' => $this->getCurrentUserId(),
|
||||
]);
|
||||
|
||||
// Invia notifica email ai responsabili
|
||||
$this->notifyIncidentStakeholders($id, 'final_report');
|
||||
|
||||
$this->logAudit('final_report_sent', 'incident', $id);
|
||||
$this->jsonSuccess(null, 'Report finale registrato');
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifica stakeholder via email per milestone incidente
|
||||
*/
|
||||
private function notifyIncidentStakeholders(int $incidentId, string $type): void
|
||||
{
|
||||
try {
|
||||
$incident = Database::fetchOne('SELECT * FROM incidents WHERE id = ?', [$incidentId]);
|
||||
$org = Database::fetchOne('SELECT * FROM organizations WHERE id = ?', [$this->getCurrentOrgId()]);
|
||||
|
||||
if (!$incident || !$org) return;
|
||||
|
||||
// Trova org_admin e compliance_manager
|
||||
$recipients = Database::fetchAll(
|
||||
'SELECT u.email, u.full_name FROM users u
|
||||
JOIN user_organizations uo ON uo.user_id = u.id
|
||||
WHERE uo.organization_id = ? AND uo.role IN ("org_admin", "compliance_manager") AND u.is_active = 1',
|
||||
[$this->getCurrentOrgId()]
|
||||
);
|
||||
|
||||
if (empty($recipients)) return;
|
||||
|
||||
$emailService = new EmailService();
|
||||
$emails = array_column($recipients, 'email');
|
||||
|
||||
match ($type) {
|
||||
'early_warning' => $emailService->sendIncidentEarlyWarning($incident, $org, $emails),
|
||||
'notification' => $emailService->sendIncidentNotification($incident, $org, $emails),
|
||||
'final_report' => $emailService->sendIncidentFinalReport($incident, $org, $emails),
|
||||
};
|
||||
} catch (Throwable $e) {
|
||||
error_log('[EMAIL_ERROR] ' . $e->getMessage());
|
||||
// Non bloccare il flusso principale per errori email
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/incidents/{id}/ai-classify
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user