diff --git a/app/Filament/Resources/ServerResource/Pages/CreateServer.php b/app/Filament/Resources/ServerResource/Pages/CreateServer.php index a19129202..78ee1eec4 100644 --- a/app/Filament/Resources/ServerResource/Pages/CreateServer.php +++ b/app/Filament/Resources/ServerResource/Pages/CreateServer.php @@ -687,7 +687,7 @@ class CreateServer extends CreateRecord ->label('Container Labels') ->keyLabel('Title') ->valueLabel('Description') - ->columnSpan(1), + ->columnSpan(3), ]), ]), ]); diff --git a/app/Filament/Resources/UserResource/Pages/EditProfile.php b/app/Filament/Resources/UserResource/Pages/EditProfile.php index 1d86f4f02..0e96f7a04 100644 --- a/app/Filament/Resources/UserResource/Pages/EditProfile.php +++ b/app/Filament/Resources/UserResource/Pages/EditProfile.php @@ -2,10 +2,12 @@ namespace App\Filament\Resources\UserResource\Pages; +use App\Exceptions\Service\User\TwoFactorAuthenticationTokenInvalid; use App\Facades\Activity; use App\Models\ActivityLog; use App\Models\ApiKey; use App\Models\User; +use App\Services\Users\ToggleTwoFactorService; use App\Services\Users\TwoFactorSetupService; use chillerlan\QRCode\Common\EccLevel; use chillerlan\QRCode\Common\Version; @@ -20,8 +22,10 @@ use Filament\Forms\Components\Select; use Filament\Forms\Components\Tabs; use Filament\Forms\Components\TagsInput; use Filament\Forms\Components\Tabs\Tab; +use Filament\Forms\Components\Textarea; use Filament\Forms\Components\TextInput; use Filament\Forms\Get; +use Filament\Notifications\Notification; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Facades\Hash; use Illuminate\Support\HtmlString; @@ -99,12 +103,26 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile if ($this->getUser()->use_totp) { return [ - Placeholder::make('2FA already enabled!'), + Placeholder::make('2fa-already-enabled') + ->label('Two Factor Authentication is currently enabled!'), + Textarea::make('backup-tokens') + ->hidden(fn () => !cache()->get("users.{$this->getUser()->id}.2fa.tokens")) + ->rows(10) + ->readOnly() + ->formatStateUsing(fn () => cache()->get("users.{$this->getUser()->id}.2fa.tokens")) + ->helperText('These will not be shown again!') + ->label('Backup Tokens:'), + TextInput::make('2fa-disable-code') + ->label('Disable 2FA') + ->helperText('Enter your current 2FA code to disable Two Factor Authentication'), ]; } $setupService = app(TwoFactorSetupService::class); - ['image_url_data' => $url] = $setupService->handle($this->getUser()); + ['image_url_data' => $url, 'secret' => $secret] = cache()->remember( + "users.{$this->getUser()->id}.2fa.state", + now()->addMinutes(5), fn () => $setupService->handle($this->getUser()) + ); $options = new QROptions([ 'svgLogo' => public_path('pelican.svg'), @@ -147,9 +165,19 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile Placeholder::make('qr') ->label('Scan QR Code') ->content(fn () => new HtmlString(" -
$image
+
$image
")) - ->default('asdfasdf'), + ->helperText('Setup Key: '. $secret), + TextInput::make('2facode') + ->label('Code') + ->requiredWith('2fapassword') + ->helperText('Scan the QR code above using your two-step authentication app, then enter the code generated.'), + TextInput::make('2fapassword') + ->label('Current Password') + ->requiredWith('2facode') + ->currentPassword() + ->password() + ->helperText('Enter your current password to verify.'), ]; }), @@ -236,4 +264,43 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile ), ]; } + + protected function handleRecordUpdate($record, $data): \Illuminate\Database\Eloquent\Model + { + if ($token = $data['2facode'] ?? null) { + /** @var ToggleTwoFactorService $service */ + $service = resolve(ToggleTwoFactorService::class); + + $tokens = $service->handle($record, $token, true); + cache()->set("users.$record->id.2fa.tokens", implode("\n", $tokens), now()->addSeconds(15)); + + $this->redirectRoute('filament.admin.auth.profile', ['tab' => '-2fa-tab']); + } + + if ($token = $data['2fa-disable-code'] ?? null) { + /** @var ToggleTwoFactorService $service */ + $service = resolve(ToggleTwoFactorService::class); + + $service->handle($record, $token, false); + + cache()->forget("users.$record->id.2fa.state"); + } + + return parent::handleRecordUpdate($record, $data); + } + + public function exception($e, $stopPropagation): void + { + if ($e instanceof TwoFactorAuthenticationTokenInvalid) { + Notification::make() + ->title('Invalid 2FA Code') + ->body($e->getMessage()) + ->color('danger') + ->icon('tabler-2fa') + ->danger() + ->send(); + + $stopPropagation(); + } + } } diff --git a/app/Services/Servers/ServerConfigurationStructureService.php b/app/Services/Servers/ServerConfigurationStructureService.php index 75fae45e0..29ea396c6 100644 --- a/app/Services/Servers/ServerConfigurationStructureService.php +++ b/app/Services/Servers/ServerConfigurationStructureService.php @@ -50,6 +50,7 @@ class ServerConfigurationStructureService 'environment' => $this->environment->handle($server), 'invocation' => $server->startup, 'skip_egg_scripts' => $server->skip_scripts, + 'labels' => $server->docker_labels, 'build' => [ 'memory_limit' => $server->memory, 'swap' => $server->swap, @@ -62,7 +63,6 @@ class ServerConfigurationStructureService 'container' => [ 'image' => $server->image, 'requires_rebuild' => false, - 'labels' => $server->docker_labels, ], 'allocations' => [ 'force_outgoing_ip' => $server->egg->force_outgoing_ip, diff --git a/app/Services/Servers/ServerCreationService.php b/app/Services/Servers/ServerCreationService.php index 57d8ad086..d555f7876 100644 --- a/app/Services/Servers/ServerCreationService.php +++ b/app/Services/Servers/ServerCreationService.php @@ -154,6 +154,7 @@ class ServerCreationService 'database_limit' => Arr::get($data, 'database_limit') ?? 0, 'allocation_limit' => Arr::get($data, 'allocation_limit') ?? 0, 'backup_limit' => Arr::get($data, 'backup_limit') ?? 0, + 'docker_labels' => Arr::get($data, 'docker_labels'), ]); }