AuditService.php

Code Hygiene Score: 93

Keine Issues gefunden.

Dependencies 2

Klassen 1

Funktionen 7

Verwendet von 7

Code

<?php

declare(strict_types=1);

namespace Infrastructure\Audit;

// @responsibility: Zentrales Audit-Logging für kritische Operationen

use PDO;

/**
 * Audit service for logging critical operations.
 *
 * Logs to ki_dev.audit_log table with structured data for:
 * - Ontology/Taxonomy changes
 * - Entity merges
 * - DSGVO deletions
 * - Schema migrations
 * - Pipeline configuration changes
 */
final class AuditService
{
    private const TABLE = 'ki_dev.audit_log';

    /**
     * @param PDO $pdo Database connection to ki_dev
     */
    public function __construct(private PDO $pdo)
    {
    }

    /**
     * Log an audit event.
     *
     * @param string               $event       Event name (e.g., 'entity.merge', 'ontology.update')
     * @param string               $targetTable Target table name (e.g., 'entities', 'ontology_classes')
     * @param int|null             $targetId    Target entity/record ID
     * @param array<string, mixed> $oldValue    Previous state (will be JSON encoded)
     * @param array<string, mixed> $newValue    New state (will be JSON encoded)
     * @param string|null          $reason      Optional reason for the change
     * @param string               $actor       Who performed the action (username or 'system')
     * @param string               $actorType   Actor type: 'user', 'system', 'pipeline'
     * @param string               $level       Log level: 'debug', 'info', 'warning', 'error'
     */
    public function log(
        string $event,
        string $targetTable,
        ?int $targetId = null,
        array $oldValue = [],
        array $newValue = [],
        ?string $reason = null,
        string $actor = 'system',
        string $actorType = 'system',
        string $level = 'info'
    ): void {
        $correlationId = $this->getCorrelationId();

        $stmt = $this->pdo->prepare(
            'INSERT INTO ' . self::TABLE . '
            (correlation_id, event, entity_type, entity_id, context, level,
             actor, actor_type, target_table, target_id, old_value, new_value, reason,
             ip_address, created_at)
            VALUES
            (:correlation_id, :event, :entity_type, :entity_id, :context, :level,
             :actor, :actor_type, :target_table, :target_id, :old_value, :new_value, :reason,
             :ip_address, NOW())'
        );

        $stmt->execute([
            'correlation_id' => $correlationId,
            'event' => $event,
            'entity_type' => $targetTable,
            'entity_id' => $targetId,
            'context' => json_encode(['target_table' => $targetTable, 'target_id' => $targetId]),
            'level' => $level,
            'actor' => $actor,
            'actor_type' => $actorType,
            'target_table' => $targetTable,
            'target_id' => $targetId,
            'old_value' => $oldValue !== [] ? json_encode($oldValue) : null,
            'new_value' => $newValue !== [] ? json_encode($newValue) : null,
            'reason' => $reason,
            'ip_address' => $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1',
        ]);
    }

    /**
     * Log an entity creation.
     *
     * @param string               $table    Table name
     * @param int                  $id       Created entity ID
     * @param array<string, mixed> $data     Created data
     * @param string               $actor    Who created it
     * @param string               $actorType Actor type
     */
    public function logCreate(
        string $table,
        int $id,
        array $data,
        string $actor = 'system',
        string $actorType = 'system'
    ): void {
        $this->log(
            event: $table . '.create',
            targetTable: $table,
            targetId: $id,
            newValue: $data,
            actor: $actor,
            actorType: $actorType
        );
    }

    /**
     * Log an entity update.
     *
     * @param string               $table    Table name
     * @param int                  $id       Updated entity ID
     * @param array<string, mixed> $oldData  Previous state
     * @param array<string, mixed> $newData  New state
     * @param string               $actor    Who updated it
     * @param string               $actorType Actor type
     */
    public function logUpdate(
        string $table,
        int $id,
        array $oldData,
        array $newData,
        string $actor = 'system',
        string $actorType = 'system'
    ): void {
        $this->log(
            event: $table . '.update',
            targetTable: $table,
            targetId: $id,
            oldValue: $oldData,
            newValue: $newData,
            actor: $actor,
            actorType: $actorType
        );
    }

    /**
     * Log an entity deletion.
     *
     * @param string               $table    Table name
     * @param int                  $id       Deleted entity ID
     * @param array<string, mixed> $data     Deleted data (for recovery)
     * @param string|null          $reason   Reason for deletion (e.g., 'DSGVO request')
     * @param string               $actor    Who deleted it
     * @param string               $actorType Actor type
     */
    public function logDelete(
        string $table,
        int $id,
        array $data,
        ?string $reason = null,
        string $actor = 'system',
        string $actorType = 'system'
    ): void {
        $this->log(
            event: $table . '.delete',
            targetTable: $table,
            targetId: $id,
            oldValue: $data,
            reason: $reason,
            actor: $actor,
            actorType: $actorType,
            level: 'warning'
        );
    }

    /**
     * Log an entity merge operation.
     *
     * @param int                  $sourceId   Source entity ID (merged into target)
     * @param int                  $targetId   Target entity ID (kept)
     * @param array<string, mixed> $sourceData Source entity data
     * @param array<string, mixed> $targetData Target entity data after merge
     * @param string               $actor      Who performed the merge
     */
    public function logMerge(
        int $sourceId,
        int $targetId,
        array $sourceData,
        array $targetData,
        string $actor = 'system'
    ): void {
        $this->log(
            event: 'entity.merge',
            targetTable: 'entities',
            targetId: $targetId,
            oldValue: ['source_id' => $sourceId, 'source_data' => $sourceData],
            newValue: ['target_id' => $targetId, 'merged_data' => $targetData],
            reason: "Merged entity #{$sourceId} into #{$targetId}",
            actor: $actor,
            actorType: 'user',
            level: 'warning'
        );
    }

    /**
     * Get or generate correlation ID for request tracing.
     */
    private function getCorrelationId(): string
    {
        // Check for existing correlation ID from middleware
        if (isset($_SERVER['HTTP_X_CORRELATION_ID'])) {
            return $_SERVER['HTTP_X_CORRELATION_ID'];
        }

        // Check session
        if (isset($_SESSION['correlation_id'])) {
            return $_SESSION['correlation_id'];
        }

        // Generate new one
        return bin2hex(random_bytes(16));
    }
}
← Übersicht Graph