From 53e20cbc2ae0334b1b77ec8fe76383710300b37a Mon Sep 17 00:00:00 2001 From: Saeed Vaziry <61919774+saeedvaziry@users.noreply.github.com> Date: Wed, 29 Jan 2025 21:00:43 +0100 Subject: [PATCH] Ask for email when generating LetsEncrypt SSLs (#452) --- app/Actions/SSL/CreateSSL.php | 7 ++++++ app/Models/Ssl.php | 11 ++++++++++ app/SSH/Services/Webserver/Nginx.php | 2 +- .../Pages/Servers/Sites/Pages/SSL/Index.php | 16 +++++++++++--- ...5_01_29_192733_add_email_to_ssls_table.php | 22 +++++++++++++++++++ resources/views/fields/alert.blade.php | 2 +- tests/Feature/SslTest.php | 4 ++++ 7 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 database/migrations/2025_01_29_192733_add_email_to_ssls_table.php diff --git a/app/Actions/SSL/CreateSSL.php b/app/Actions/SSL/CreateSSL.php index 11a025b..ebe2861 100644 --- a/app/Actions/SSL/CreateSSL.php +++ b/app/Actions/SSL/CreateSSL.php @@ -30,6 +30,7 @@ public function create(Site $site, array $input): void 'pk' => $input['private'] ?? null, 'expires_at' => $input['type'] === SslType::LETSENCRYPT ? now()->addMonths(3) : $input['expires_at'], 'status' => SslStatus::CREATING, + 'email' => $input['email'] ?? null, ]); $ssl->domains = [$site->domain]; if (isset($input['aliases']) && $input['aliases']) { @@ -69,6 +70,12 @@ public static function rules(array $input): array 'after_or_equal:'.now(), ]; } + if (isset($input['type']) && $input['type'] == SslType::LETSENCRYPT) { + $rules['email'] = [ + 'required', + 'email', + ]; + } return $rules; } diff --git a/app/Models/Ssl.php b/app/Models/Ssl.php index cb16017..fb827b6 100644 --- a/app/Models/Ssl.php +++ b/app/Models/Ssl.php @@ -20,6 +20,7 @@ * @property string $ca_path * @property ?array $domains * @property int $log_id + * @property string $email * @property ?ServerLog $log */ class Ssl extends AbstractModel @@ -36,6 +37,7 @@ class Ssl extends AbstractModel 'status', 'domains', 'log_id', + 'email', ]; protected $casts = [ @@ -143,4 +145,13 @@ public function log(): BelongsTo { return $this->belongsTo(ServerLog::class); } + + public function getEmailAttribute(?string $value): string + { + if ($value) { + return $value; + } + + return $this->site->server->creator->email; + } } diff --git a/app/SSH/Services/Webserver/Nginx.php b/app/SSH/Services/Webserver/Nginx.php index 3b08356..3c85ab0 100755 --- a/app/SSH/Services/Webserver/Nginx.php +++ b/app/SSH/Services/Webserver/Nginx.php @@ -168,7 +168,7 @@ public function setupSSL(Ssl $ssl): void $domains .= ' -d '.$domain; } $command = view('ssh.services.webserver.nginx.create-letsencrypt-ssl', [ - 'email' => $ssl->site->server->creator->email, + 'email' => $ssl->email, 'domain' => $ssl->site->domain, 'domains' => $domains, ]); diff --git a/app/Web/Pages/Servers/Sites/Pages/SSL/Index.php b/app/Web/Pages/Servers/Sites/Pages/SSL/Index.php index c9bc5b0..1b24af9 100644 --- a/app/Web/Pages/Servers/Sites/Pages/SSL/Index.php +++ b/app/Web/Pages/Servers/Sites/Pages/SSL/Index.php @@ -3,7 +3,9 @@ namespace App\Web\Pages\Servers\Sites\Pages\SSL; use App\Actions\SSL\CreateSSL; +use App\Enums\SslType; use App\Models\Ssl; +use App\Web\Fields\AlertField; use App\Web\Pages\Servers\Sites\Page; use Filament\Actions\Action; use Filament\Actions\CreateAction; @@ -11,6 +13,7 @@ use Filament\Forms\Components\DatePicker; use Filament\Forms\Components\Select; use Filament\Forms\Components\Textarea; +use Filament\Forms\Components\TextInput; use Filament\Forms\Get; use Filament\Support\Enums\MaxWidth; @@ -45,25 +48,32 @@ protected function getHeaderActions(): array ->label('New Certificate') ->icon('heroicon-o-lock-closed') ->form([ + AlertField::make('letsencrypt-info') + ->warning() + ->message('Let\'s Encrypt has rate limits. Read more about them here.'), Select::make('type') ->options( collect(config('core.ssl_types'))->mapWithKeys(fn ($type) => [$type => $type]) ) ->rules(fn (Get $get) => CreateSSL::rules($get())['type']) ->reactive(), + TextInput::make('email') + ->rules(fn (Get $get) => CreateSSL::rules($get())['email'] ?? []) + ->visible(fn (Get $get) => $get('type') === SslType::LETSENCRYPT) + ->helperText('Email address to provide to Certbot.'), Textarea::make('certificate') ->rows(5) ->rules(fn (Get $get) => CreateSSL::rules($get())['certificate']) - ->visible(fn (Get $get) => $get('type') === 'custom'), + ->visible(fn (Get $get) => $get('type') === SslType::CUSTOM), Textarea::make('private') ->label('Private Key') ->rows(5) ->rules(fn (Get $get) => CreateSSL::rules($get())['private']) - ->visible(fn (Get $get) => $get('type') === 'custom'), + ->visible(fn (Get $get) => $get('type') === SslType::CUSTOM), DatePicker::make('expires_at') ->format('Y-m-d') ->rules(fn (Get $get) => CreateSSL::rules($get())['expires_at']) - ->visible(fn (Get $get) => $get('type') === 'custom'), + ->visible(fn (Get $get) => $get('type') === SslType::CUSTOM), Checkbox::make('aliases') ->label("Set SSL for site's aliases as well"), ]) diff --git a/database/migrations/2025_01_29_192733_add_email_to_ssls_table.php b/database/migrations/2025_01_29_192733_add_email_to_ssls_table.php new file mode 100644 index 0000000..244c5a0 --- /dev/null +++ b/database/migrations/2025_01_29_192733_add_email_to_ssls_table.php @@ -0,0 +1,22 @@ +string('email')->nullable(); + }); + } + + public function down(): void + { + Schema::table('ssls', function (Blueprint $table) { + $table->dropColumn('email'); + }); + } +}; diff --git a/resources/views/fields/alert.blade.php b/resources/views/fields/alert.blade.php index a0c0b90..f06ae34 100644 --- a/resources/views/fields/alert.blade.php +++ b/resources/views/fields/alert.blade.php @@ -3,7 +3,7 @@