Files
michaelschiemer/docs/FILEPERMISSION_EXCEPTION_IMPROVEMENT_PLAN.md
Michael Schiemer 3085739e34 feat(filesystem): introduce FileOwnership and ProcessUser value objects
- Add `FileOwnership` to encapsulate file owner and group information.
- Add `ProcessUser` to represent and manage system process user details.
- Enhance ownership matching and debugging with structured data objects.
- Include new documentation on file ownership handling and permission improvements.
- Prepare infrastructure for enriched error handling in filesystem operations.
2025-11-04 00:56:49 +01:00

6.7 KiB

FilePermissionException Verbesserungs-Plan

Ziel

Verbesserung der FilePermissionException um detaillierte Permission- und User-Informationen f?r besseres Debugging.

Problem-Analyse

Aktueller Zustand

Die FilePermissionException gibt momentan nur minimale Informationen:

FilePermissionException::delete($path, 'Directory is not writable: ' . $dir);
// Output: "Permission denied for delete on file: /path/to/file (Directory is not writable: /path/to/dir)"

Fehlende Informationen:

  • Aktuelle Permissions der Datei/Verzeichnisses
  • Owner/Group der Datei
  • Aktueller Process-User
  • Erwartete Permissions
  • Octal-Darstellung der Permissions

Verf?gbare Infrastruktur

? PermissionChecker hat bereits getDiagnosticInfo():

  • Liefert owner, group, permissions, parent_dir info
  • Nutzt posix_getpwuid() und posix_getgrgid()

? ExceptionContext hat withData(), withDebug(), withMetadata()

? posix_geteuid() verf?gbar f?r Process-User

L?sungsplan

L?sung 1: Erweitere FilePermissionException (HOHE PRIORIT?T)

Neue statische Factory-Methoden mit Permission-Details:

public static function delete(
    string $path, 
    ?string $reason = null,
    ?PermissionChecker $permissionChecker = null
): self {
    $diagnosticInfo = $permissionChecker?->getDiagnosticInfo($path) ?? [];
    $currentUser = self::getCurrentUserInfo();
    
    return new self(
        path: $path,
        operation: 'delete',
        reason: $reason,
        diagnosticInfo: $diagnosticInfo,
        currentUser: $currentUser
    );
}

Neue Properties:

  • ?array $diagnosticInfo - Permission-Details vom PermissionChecker
  • ?array $currentUser - Aktueller Process-User Info

Verbesserte Message-Formatierung:

$message = "Permission denied for {$operation} on file: {$path}";
if ($reason) {
    $message .= " ({$reason})";
}

// Add detailed info if available
if ($this->diagnosticInfo) {
    $message .= sprintf(
        "\n  File owner: %s, group: %s, permissions: %s",
        $this->diagnosticInfo['owner'] ?? 'unknown',
        $this->diagnosticInfo['group'] ?? 'unknown',
        $this->diagnosticInfo['permissions'] ?? 'unknown'
    );
    
    if (isset($this->diagnosticInfo['parent_dir'])) {
        $parentInfo = $permissionChecker?->getDiagnosticInfo($this->diagnosticInfo['parent_dir']) ?? [];
        $message .= sprintf(
            "\n  Parent directory: %s (owner: %s, permissions: %s)",
            $this->diagnosticInfo['parent_dir'],
            $parentInfo['owner'] ?? 'unknown',
            $parentInfo['permissions'] ?? 'unknown'
        );
    }
}

if ($this->currentUser) {
    $message .= sprintf(
        "\n  Current process user: %s (uid: %d, gid: %d)",
        $this->currentUser['name'],
        $this->currentUser['uid'],
        $this->currentUser['gid']
    );
}

L?sung 2: Helper-Methode f?r Process-User Info

Neue private Methode in FilePermissionException:

private static function getCurrentUserInfo(): ?array
{
    if (!function_exists('posix_geteuid')) {
        return null;
    }
    
    $uid = posix_geteuid();
    $gid = posix_getegid();
    $userInfo = posix_getpwuid($uid);
    $groupInfo = posix_getgrgid($gid);
    
    return [
        'uid' => $uid,
        'gid' => $gid,
        'name' => $userInfo['name'] ?? 'unknown',
        'group' => $groupInfo['name'] ?? 'unknown',
    ];
}

L?sung 3: Integration in FileStorage-Methoden

Anpassung aller FilePermissionException Aufrufe:

Vorher:

if (! is_writable($dir)) {
    throw FilePermissionException::delete($path, 'Directory is not writable: ' . $dir);
}

Nachher:

if (! is_writable($dir)) {
    throw FilePermissionException::delete(
        path: $path,
        reason: 'Directory is not writable: ' . $dir,
        permissionChecker: $this->permissions
    );
}

Betroffene Stellen:

  1. FileStorage::get() - read exception
  2. FileStorage::put() - write exception, createDirectory exception
  3. FileStorage::delete() - delete exception
  4. FileStorage::createDirectory() - createDirectory exception
  5. Alle anderen Stellen, die FilePermissionException werfen

L?sung 4: ExceptionContext erweitern

Erweiterte Context-Daten in Exception:

$context = ExceptionContext::forOperation('file.permission', 'filesystem')
    ->withData([
        'path' => $path,
        'operation' => $operation,
        'reason' => $reason,
    ])
    ->withDebug([
        'file_permissions' => $diagnosticInfo['permissions'] ?? null,
        'file_owner' => $diagnosticInfo['owner'] ?? null,
        'file_group' => $diagnosticInfo['group'] ?? null,
        'parent_dir' => $diagnosticInfo['parent_dir'] ?? null,
        'parent_writable' => $diagnosticInfo['parent_writable'] ?? null,
    ])
    ->withMetadata([
        'current_user' => $currentUser,
        'file_exists' => $diagnosticInfo['exists'] ?? false,
        'is_file' => $diagnosticInfo['is_file'] ?? false,
        'is_dir' => $diagnosticInfo['is_dir'] ?? false,
    ]);

Implementierungsreihenfolge

  1. ? L?sung 2 - Helper-Methode f?r Process-User (einfach, keine Dependencies)
  2. ? L?sung 1 - Erweitere FilePermissionException Constructor und Factory-Methoden
  3. ? L?sung 3 - Integration in FileStorage-Methoden
  4. ? L?sung 4 - ExceptionContext erweitern

Beispiel-Output

Vorher:

Permission denied for delete on file: /var/www/html/storage/cache/file.cache.php (Directory is not writable: /var/www/html/storage/cache)

Nachher:

Permission denied for delete on file: /var/www/html/storage/cache/file.cache.php (Directory is not writable: /var/www/html/storage/cache)
  File owner: www-data, group: www-data, permissions: drwxrwxr-x
  Parent directory: /var/www/html/storage/cache (owner: root, permissions: drwxr-xr-x)
  Current process user: www-data (uid: 33, gid: 33)

Vorteile

  1. Besseres Debugging: Sofort sichtbar, welche Permissions falsch sind
  2. User-Info: Zeigt Owner-Mismatch sofort
  3. Context: Vollst?ndige Permission-Info in Exception-Context f?r Logging
  4. R?ckw?rtskompatibel: Alte Aufrufe funktionieren weiterhin (optional Parameter)

Performance-?berlegungen

  • getDiagnosticInfo() ist relativ teuer (mehrere stat() Calls)
  • L?sung: Nur bei Exception-Throw aufrufen (nicht bei jedem Check)
  • Optional: K?nnte in withDebug() ausgelagert werden, wenn Performance kritisch ist

Testing

  1. Unit-Tests: FilePermissionException mit Permission-Details
  2. Integration-Tests: FileStorage-Methoden mit verschiedenen Permission-Szenarien
  3. E2E-Tests: Cache-Operationen mit Permission-Problemen

Dokumentation

Nach Implementierung aktualisieren:

  • docs/claude/error-handling.md - FilePermissionException mit Details
  • docs/deployment/cache-configuration.md - Exception-Beispiele