Files
jabali-panel/app/Console/Commands/ManageGitProtection.php
2026-02-02 03:11:45 +02:00

234 lines
7.0 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
class ManageGitProtection extends Command
{
protected $signature = 'jabali:git-protection
{action : enable|disable|status|add-committer|remove-committer|list-committers}
{--email= : Email address for add/remove committer}';
protected $description = 'Manage Git commit protection for Jabali';
protected string $authFile;
protected string $hookFile;
protected string $deployKeyFile;
public function __construct()
{
parent::__construct();
$this->authFile = base_path('.git-authorized-committers');
$this->hookFile = base_path('.git/hooks/pre-commit');
$this->deployKeyFile = base_path('.deploy-key');
}
public function handle(): int
{
$action = $this->argument('action');
return match ($action) {
'enable' => $this->enableProtection(),
'disable' => $this->disableProtection(),
'status' => $this->showStatus(),
'add-committer' => $this->addCommitter(),
'remove-committer' => $this->removeCommitter(),
'list-committers' => $this->listCommitters(),
default => $this->showHelp(),
};
}
protected function enableProtection(): int
{
// Ensure hook exists and is executable
if (!file_exists($this->hookFile)) {
$this->error('Pre-commit hook not found. Please reinstall Jabali.');
return 1;
}
chmod($this->hookFile, 0755);
// Create authorized committers file if not exists
if (!file_exists($this->authFile)) {
// Add current git user as first authorized committer
$email = trim(shell_exec('git config user.email') ?? '');
if ($email) {
file_put_contents($this->authFile, $email . "\n");
} else {
touch($this->authFile);
}
chmod($this->authFile, 0600);
}
// Generate deploy key for automated deployments
if (!file_exists($this->deployKeyFile)) {
$key = Str::random(64);
file_put_contents($this->deployKeyFile, $key);
chmod($this->deployKeyFile, 0600);
$this->info("Deploy key generated. Use JABALI_DEPLOY_KEY=$key for automated deployments.");
}
$this->info('Git commit protection ENABLED.');
$this->line('Only authorized committers can now make commits.');
$this->line('');
$this->line('Authorized committers file: ' . $this->authFile);
return 0;
}
protected function disableProtection(): int
{
if (file_exists($this->authFile)) {
unlink($this->authFile);
}
$this->info('Git commit protection DISABLED.');
$this->line('Anyone can now make commits to this repository.');
return 0;
}
protected function showStatus(): int
{
$isEnabled = file_exists($this->authFile);
$this->line('Git Protection Status');
$this->line('=====================');
$this->line('');
if ($isEnabled) {
$this->info('Status: ENABLED');
$this->line('');
$committers = $this->getCommitters();
if (count($committers) > 0) {
$this->line('Authorized committers:');
foreach ($committers as $email) {
$this->line(" - $email");
}
} else {
$this->warn('No authorized committers configured!');
$this->line('No one will be able to commit.');
}
if (file_exists($this->deployKeyFile)) {
$this->line('');
$this->line('Deploy key: Configured');
}
} else {
$this->warn('Status: DISABLED');
$this->line('Anyone can make commits.');
}
// Show last integrity check status
$lastCheck = \App\Models\DnsSetting::get('last_integrity_check');
$lastStatus = \App\Models\DnsSetting::get('last_integrity_status');
if ($lastCheck) {
$this->line('');
$this->line('Last integrity check: ' . $lastCheck);
$this->line('Status: ' . ($lastStatus === 'clean' ? 'Clean' : 'Modified files detected'));
}
return 0;
}
protected function addCommitter(): int
{
$email = $this->option('email');
if (!$email) {
$email = $this->ask('Enter committer email address');
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->error('Invalid email address.');
return 1;
}
$committers = $this->getCommitters();
if (in_array($email, $committers)) {
$this->warn("$email is already an authorized committer.");
return 0;
}
$committers[] = $email;
file_put_contents($this->authFile, implode("\n", $committers) . "\n");
chmod($this->authFile, 0600);
$this->info("Added $email to authorized committers.");
return 0;
}
protected function removeCommitter(): int
{
$email = $this->option('email');
if (!$email) {
$email = $this->ask('Enter committer email to remove');
}
$committers = $this->getCommitters();
$committers = array_filter($committers, fn($e) => $e !== $email);
file_put_contents($this->authFile, implode("\n", $committers) . "\n");
$this->info("Removed $email from authorized committers.");
return 0;
}
protected function listCommitters(): int
{
$committers = $this->getCommitters();
if (empty($committers)) {
$this->warn('No authorized committers configured.');
return 0;
}
$this->info('Authorized committers:');
foreach ($committers as $email) {
$this->line(" - $email");
}
return 0;
}
protected function getCommitters(): array
{
if (!file_exists($this->authFile)) {
return [];
}
$content = file_get_contents($this->authFile);
$lines = array_filter(array_map('trim', explode("\n", $content)));
return $lines;
}
protected function showHelp(): int
{
$this->line('Usage: php artisan jabali:git-protection <action>');
$this->line('');
$this->line('Actions:');
$this->line(' enable Enable commit protection');
$this->line(' disable Disable commit protection');
$this->line(' status Show protection status');
$this->line(' add-committer Add an authorized committer');
$this->line(' remove-committer Remove an authorized committer');
$this->line(' list-committers List all authorized committers');
$this->line('');
$this->line('Options:');
$this->line(' --email=<email> Email address for add/remove actions');
return 0;
}
}