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

227 lines
6.7 KiB
Markdown

# 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:
```php
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:**
```php
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:**
```php
$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:**
```php
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:**
```php
if (! is_writable($dir)) {
throw FilePermissionException::delete($path, 'Directory is not writable: ' . $dir);
}
```
**Nachher:**
```php
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:**
```php
$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