- 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.
227 lines
6.7 KiB
Markdown
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
|