- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
211 lines
6.3 KiB
PHP
211 lines
6.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Filesystem;
|
|
|
|
use App\Framework\Core\PathProvider;
|
|
use App\Framework\Filesystem\ValueObjects\FilePermissions;
|
|
|
|
/**
|
|
* Helper-Klasse für Filesystem-Permissions-Checks
|
|
*/
|
|
final readonly class PermissionChecker
|
|
{
|
|
public function __construct(
|
|
private PathProvider $pathProvider
|
|
) {
|
|
}
|
|
|
|
private function resolvePath(string $path): string
|
|
{
|
|
// Absolute paths bleiben unverändert
|
|
if (str_starts_with($path, '/')) {
|
|
return $path;
|
|
}
|
|
|
|
// Relative paths über PathProvider auflösen (mit Caching)
|
|
return $this->pathProvider->resolvePath($path);
|
|
}
|
|
|
|
public function isDirectoryWritable(string $path): bool
|
|
{
|
|
$resolvedPath = $this->resolvePath($path);
|
|
if (! is_dir($resolvedPath)) {
|
|
return false;
|
|
}
|
|
|
|
return is_writable($resolvedPath);
|
|
}
|
|
|
|
public function canCreateDirectory(string $path): bool
|
|
{
|
|
$resolvedPath = $this->resolvePath($path);
|
|
// Prüfe, ob Parent-Directory existiert und schreibbar ist
|
|
$parentDir = dirname($resolvedPath);
|
|
|
|
if (! is_dir($parentDir)) {
|
|
return $this->canCreateDirectory(dirname($path));
|
|
}
|
|
|
|
return is_writable($parentDir);
|
|
}
|
|
|
|
public function canWriteFile(string $path): bool
|
|
{
|
|
$resolvedPath = $this->resolvePath($path);
|
|
// Wenn Datei existiert, prüfe ob sie schreibbar ist
|
|
if (file_exists($resolvedPath)) {
|
|
return is_writable($resolvedPath);
|
|
}
|
|
|
|
// Wenn Datei nicht existiert, prüfe ob Directory schreibbar ist
|
|
$dir = dirname($path);
|
|
|
|
return $this->isDirectoryWritable($dir) || $this->canCreateDirectory($dir);
|
|
}
|
|
|
|
public function canReadFile(string $path): bool
|
|
{
|
|
$resolvedPath = $this->resolvePath($path);
|
|
|
|
return file_exists($resolvedPath) && is_readable($resolvedPath);
|
|
}
|
|
|
|
public function canDeleteFile(string $path): bool
|
|
{
|
|
$resolvedPath = $this->resolvePath($path);
|
|
if (! file_exists($resolvedPath)) {
|
|
return false;
|
|
}
|
|
|
|
// Datei muss schreibbar sein UND Directory muss schreibbar sein
|
|
$dir = dirname($resolvedPath);
|
|
|
|
return is_writable($resolvedPath) && is_writable($dir);
|
|
}
|
|
|
|
public function getPermissionString(string $path): string
|
|
{
|
|
$resolvedPath = $this->resolvePath($path);
|
|
if (! file_exists($resolvedPath)) {
|
|
return 'not found';
|
|
}
|
|
|
|
$perms = fileperms($resolvedPath);
|
|
$info = '';
|
|
|
|
// Type
|
|
if (($perms & 0xC000) == 0xC000) {
|
|
$info = 's'; // Socket
|
|
} elseif (($perms & 0xA000) == 0xA000) {
|
|
$info = 'l'; // Symbolic Link
|
|
} elseif (($perms & 0x8000) == 0x8000) {
|
|
$info = 'f'; // Regular file
|
|
} elseif (($perms & 0x6000) == 0x6000) {
|
|
$info = 'b'; // Block special
|
|
} elseif (($perms & 0x4000) == 0x4000) {
|
|
$info = 'd'; // Directory
|
|
} elseif (($perms & 0x2000) == 0x2000) {
|
|
$info = 'c'; // Character special
|
|
} elseif (($perms & 0x1000) == 0x1000) {
|
|
$info = 'p'; // FIFO pipe
|
|
} else {
|
|
$info = 'u'; // Unknown
|
|
}
|
|
|
|
// Owner
|
|
$info .= (($perms & 0x0100) ? 'r' : '-');
|
|
$info .= (($perms & 0x0080) ? 'w' : '-');
|
|
$info .= (($perms & 0x0040) ?
|
|
(($perms & 0x0800) ? 's' : 'x') :
|
|
(($perms & 0x0800) ? 'S' : '-'));
|
|
|
|
// Group
|
|
$info .= (($perms & 0x0020) ? 'r' : '-');
|
|
$info .= (($perms & 0x0010) ? 'w' : '-');
|
|
$info .= (($perms & 0x0008) ?
|
|
(($perms & 0x0400) ? 's' : 'x') :
|
|
(($perms & 0x0400) ? 'S' : '-'));
|
|
|
|
// World
|
|
$info .= (($perms & 0x0004) ? 'r' : '-');
|
|
$info .= (($perms & 0x0002) ? 'w' : '-');
|
|
$info .= (($perms & 0x0001) ?
|
|
(($perms & 0x0200) ? 't' : 'x') :
|
|
(($perms & 0x0200) ? 'T' : '-'));
|
|
|
|
return $info;
|
|
}
|
|
|
|
public function getDiagnosticInfo(string $path): array
|
|
{
|
|
$resolvedPath = $this->resolvePath($path);
|
|
$realPath = realpath($resolvedPath) ?: $resolvedPath;
|
|
|
|
return [
|
|
'path' => $path,
|
|
'resolved_path' => $resolvedPath,
|
|
'real_path' => $realPath,
|
|
'exists' => file_exists($resolvedPath),
|
|
'is_file' => is_file($resolvedPath),
|
|
'is_dir' => is_dir($resolvedPath),
|
|
'is_readable' => is_readable($resolvedPath),
|
|
'is_writable' => is_writable($resolvedPath),
|
|
'permissions' => $this->getPermissionString($path),
|
|
'owner' => file_exists($resolvedPath) ? posix_getpwuid(fileowner($resolvedPath))['name'] ?? 'unknown' : null,
|
|
'group' => file_exists($resolvedPath) ? posix_getgrgid(filegroup($resolvedPath))['name'] ?? 'unknown' : null,
|
|
'parent_dir' => dirname($resolvedPath),
|
|
'parent_writable' => is_dir(dirname($resolvedPath)) && is_writable(dirname($resolvedPath)),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get current file permissions as FilePermissions value object
|
|
*/
|
|
public function getPermissions(string $path): ?FilePermissions
|
|
{
|
|
$resolvedPath = $this->resolvePath($path);
|
|
if (! file_exists($resolvedPath)) {
|
|
return null;
|
|
}
|
|
|
|
$perms = fileperms($resolvedPath);
|
|
// Extract only permission bits (last 9 bits)
|
|
$mode = $perms & 0777;
|
|
|
|
return new FilePermissions($mode);
|
|
}
|
|
|
|
/**
|
|
* Set file permissions using FilePermissions value object
|
|
*/
|
|
public function setPermissions(string $path, FilePermissions $permissions): bool
|
|
{
|
|
$resolvedPath = $this->resolvePath($path);
|
|
if (! file_exists($resolvedPath)) {
|
|
return false;
|
|
}
|
|
|
|
return @chmod($resolvedPath, $permissions->mode);
|
|
}
|
|
|
|
/**
|
|
* Ensure file has specific permissions, set them if different
|
|
*/
|
|
public function ensurePermissions(string $path, FilePermissions $permissions): bool
|
|
{
|
|
$current = $this->getPermissions($path);
|
|
|
|
if ($current === null) {
|
|
return false; // File doesn't exist
|
|
}
|
|
|
|
if ($current->equals($permissions)) {
|
|
return true; // Already has correct permissions
|
|
}
|
|
|
|
return $this->setPermissions($path, $permissions);
|
|
}
|
|
}
|