d2d.emegrendeles.hu/app/Services/Deployment/DeploymentGuard.php
E98Developer 09eed3eeb3 ADD Deployment csomagoló phase1 védelem, git olvasó és read-only commit/diff felület
Két commit közötti fájlok összegyűjtését előkészítő felület első fázisa: célkörnyezet
választás (e2e/d2d), commitlista, diff-előnézet figyelmeztetésekkel. Ez a fázis még
semmit nem ír a fájlrendszerre, a csomagolás a phase2-ben érkezik.

- DeploymentGuard: .env kapcsoló + hardkódolt stage whitelist + developer szerep +
  feature flag; a flag szándékosan nem biztonsági réteg, csak láthatóság-vezérlés
- GitRepository: csak olvasó wrapper, argumentum-tömbös Symfony Process (nincs shell),
  hash- és referencia-validáció, core.quotePath=false az ékezetes útvonalakhoz
- ChangeSetAnalyzer: kizárási lista, törlendő/másolandó szétválasztás (átnevezésnél
  mindkettő), figyelmeztetések migrációra, composer.lock-ra és a nem verziókövetett
  fordított assetekre

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 06:45:09 +02:00

70 lines
2.4 KiB
PHP

<?php
namespace App\Services\Deployment;
use App\Models\User;
use Laravel\Pennant\Feature;
/**
* A deployment csomagoló hozzáférés-ellenőrzése egy helyen.
*
* Az alkalmazás kódja manuális fájlmásolással kerül a szerverekre, tehát ez az oldal
* fizikailag ott lesz az éles környezetben is. Ezért négy, egymástól független feltétel
* van, és mindet MINDEN belépési ponton ellenőrizni kell (oldal mount, akciók, később a
* csomagoló service) - ha csak a felületet védjük, az látszatvédelem.
*
* A négyből a tényleges védelmet az .env kapcsoló és a stage whitelist adja, a
* jogosultságot a szerepkör. A feature flag NEM biztonsági réteg: a menü-láthatóságot
* vezérli, és lehetővé teszi a deploy nélküli kikapcsolást, illetve a név szerinti
* átadást (feature_flag_overrides) developer szerep osztogatása nélkül.
*/
class DeploymentGuard
{
public function isAllowed(?User $user = null): bool
{
return $this->denialReason($user) === null;
}
/**
* Az első nem teljesülő feltétel magyarázata, vagy null, ha minden rendben.
*/
public function denialReason(?User $user = null): ?string
{
if (! config('deployment.enabled')) {
return 'A deployment csomagoló ki van kapcsolva (DEPLOYMENT_PACKAGE_ENABLED).';
}
/** @var array<int, string> $stages */
$stages = (array) config('deployment.stages', []);
if (! in_array((string) config('app.stage'), $stages, true)) {
return sprintf(
'A deployment csomagoló csak fejlesztői környezetben használható (engedélyezett stage: %s, jelenlegi: %s).',
implode(', ', $stages),
(string) config('app.stage'),
);
}
if (! $user) {
return 'Bejelentkezés szükséges.';
}
if (! $user->hasRole((string) config('deployment.role'))) {
return 'A deployment csomagoló csak fejlesztői szerepkörrel érhető el.';
}
if (! Feature::for($user)->active((string) config('deployment.feature_flag'))) {
return 'A deployment csomagoló feature flag nincs bekapcsolva erre a felhasználóra.';
}
return null;
}
public function ensureAllowed(?User $user = null): void
{
if ($reason = $this->denialReason($user)) {
abort(403, $reason);
}
}
}