From 12a4813e73106a1c1857b6354a13112c525203f6 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Thu, 12 Dec 2024 09:42:01 +0100 Subject: [PATCH] add own action class for "rotate database password" --- .../DatabasesRelationManager.php | 32 +++------- .../ServerResource/Pages/EditServer.php | 28 ++------- .../Actions/RotateDatabasePasswordAction.php | 62 +++++++++++++++++++ .../DatabaseResource/Pages/ListDatabases.php | 18 ++---- app/Models/Database.php | 9 +++ .../Databases/DatabasePasswordService.php | 4 +- 6 files changed, 89 insertions(+), 64 deletions(-) create mode 100644 app/Filament/Components/Forms/Actions/RotateDatabasePasswordAction.php diff --git a/app/Filament/Admin/Resources/DatabaseHostResource/RelationManagers/DatabasesRelationManager.php b/app/Filament/Admin/Resources/DatabaseHostResource/RelationManagers/DatabasesRelationManager.php index ecc13de502..f13cff5666 100644 --- a/app/Filament/Admin/Resources/DatabaseHostResource/RelationManagers/DatabasesRelationManager.php +++ b/app/Filament/Admin/Resources/DatabaseHostResource/RelationManagers/DatabasesRelationManager.php @@ -2,14 +2,11 @@ namespace App\Filament\Admin\Resources\DatabaseHostResource\RelationManagers; +use App\Filament\Components\Forms\Actions\RotateDatabasePasswordAction; use App\Models\Database; -use App\Services\Databases\DatabasePasswordService; use App\Tables\Columns\DateTimeColumn; -use Filament\Forms\Components\Actions\Action; use Filament\Forms\Components\TextInput; use Filament\Forms\Form; -use Filament\Forms\Get; -use Filament\Forms\Set; use Filament\Resources\RelationManagers\RelationManager; use Filament\Tables\Actions\DeleteAction; use Filament\Tables\Actions\ViewAction; @@ -30,25 +27,19 @@ public function form(Form $form): Form TextInput::make('password') ->password() ->revealable() - ->hintAction( - Action::make('rotate') - ->icon('tabler-refresh') - ->requiresConfirmation() - ->action(fn (DatabasePasswordService $service, Database $database, $set, $get) => $this->rotatePassword($service, $database, $set, $get)) - ->authorize(fn (Database $database) => auth()->user()->can('update database', $database)) - ) + ->hintAction(RotateDatabasePasswordAction::make()) ->formatStateUsing(fn (Database $database) => $database->password), TextInput::make('remote') ->label('Connections From') - ->formatStateUsing(fn ($record) => $record->remote === '%' ? 'Anywhere ( % )' : $record->remote), + ->formatStateUsing(fn (Database $record) => $record->remote === '%' ? 'Anywhere ( % )' : $record->remote), TextInput::make('max_connections') - ->formatStateUsing(fn ($record) => $record->max_connections === 0 ? 'Unlimited' : $record->max_connections), - TextInput::make('JDBC') + ->formatStateUsing(fn (Database $record) => $record->max_connections === 0 ? 'Unlimited' : $record->max_connections), + TextInput::make('jdbc') ->label('JDBC Connection String') ->columnSpanFull() ->password() ->revealable() - ->formatStateUsing(fn (Get $get, Database $database) => 'jdbc:mysql://' . $get('username') . ':' . urlencode($database->password) . '@' . $database->host->host . ':' . $database->host->port . '/' . $get('database')), + ->formatStateUsing(fn (Database $database) => $database->jdbc), ]); } @@ -62,7 +53,7 @@ public function table(Table $table): Table TextColumn::make('username') ->icon('tabler-user'), TextColumn::make('remote') - ->formatStateUsing(fn ($record) => $record->remote === '%' ? 'Anywhere ( % )' : $record->remote), + ->formatStateUsing(fn (Database $record) => $record->remote === '%' ? 'Anywhere ( % )' : $record->remote), TextColumn::make('server.name') ->icon('tabler-brand-docker') ->url(fn (Database $database) => route('filament.admin.resources.servers.edit', ['record' => $database->server_id])), @@ -78,13 +69,4 @@ public function table(Table $table): Table ->hidden(fn () => !auth()->user()->can('viewList database')), ]); } - - protected function rotatePassword(DatabasePasswordService $service, Database $database, Set $set, Get $get): void - { - $newPassword = $service->handle($database); - $jdbcString = 'jdbc:mysql://' . $get('username') . ':' . urlencode($newPassword) . '@' . $database->host->host . ':' . $database->host->port . '/' . $get('database'); - - $set('password', $newPassword); - $set('JDBC', $jdbcString); - } } diff --git a/app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php b/app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php index 05401cb2f5..bad9e62e4d 100644 --- a/app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php +++ b/app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php @@ -6,6 +6,7 @@ use App\Enums\ServerState; use App\Filament\Admin\Resources\ServerResource; use App\Filament\Admin\Resources\ServerResource\RelationManagers\AllocationsRelationManager; +use App\Filament\Components\Forms\Actions\RotateDatabasePasswordAction; use App\Filament\Server\Pages\Console; use App\Models\Database; use App\Models\DatabaseHost; @@ -14,7 +15,6 @@ use App\Models\Server; use App\Models\ServerVariable; use App\Services\Databases\DatabaseManagementService; -use App\Services\Databases\DatabasePasswordService; use App\Services\Eggs\EggChangerService; use App\Services\Servers\RandomWordService; use App\Services\Servers\ReinstallServerService; @@ -667,31 +667,24 @@ public function form(Form $form): Form ->password() ->revealable() ->columnSpan(1) - ->hintAction( - Action::make('rotate') - ->authorize(fn (Database $database) => auth()->user()->can('update database', $database)) - ->icon('tabler-refresh') - ->modalHeading('Change Database Password?') - ->action(fn (DatabasePasswordService $service, $record, $set, $get) => $this->rotatePassword($service, $record, $set, $get)) - ->requiresConfirmation() - ) + ->hintAction(RotateDatabasePasswordAction::make()) ->formatStateUsing(fn (Database $database) => $database->password), TextInput::make('remote') ->disabled() - ->formatStateUsing(fn ($record) => $record->remote === '%' ? 'Anywhere ( % )' : $record->remote) + ->formatStateUsing(fn (Database $record) => $record->remote === '%' ? 'Anywhere ( % )' : $record->remote) ->columnSpan(1) ->label('Connections From'), TextInput::make('max_connections') ->disabled() - ->formatStateUsing(fn ($record) => $record->max_connections === 0 ? 'Unlimited' : $record->max_connections) + ->formatStateUsing(fn (Database $record) => $record->max_connections === 0 ? 'Unlimited' : $record->max_connections) ->columnSpan(1), - TextInput::make('JDBC') + TextInput::make('jdbc') ->disabled() ->password() ->revealable() ->label('JDBC Connection String') ->columnSpan(2) - ->formatStateUsing(fn (Get $get, $record) => 'jdbc:mysql://' . $get('username') . ':' . urlencode($record->password) . '@' . $record->host->host . ':' . $record->host->port . '/' . $get('database')), + ->formatStateUsing(fn (Database $record) => $record->jdbc), ]) ->relationship('databases') ->deletable(false) @@ -950,13 +943,4 @@ private function getSelectOptionsFromRules(ServerVariable $serverVariable): arra ->mapWithKeys(fn ($value) => [$value => $value]) ->all(); } - - protected function rotatePassword(DatabasePasswordService $service, Database $record, Set $set, Get $get): void - { - $newPassword = $service->handle($record); - $jdbcString = 'jdbc:mysql://' . $get('username') . ':' . urlencode($newPassword) . '@' . $record->host->host . ':' . $record->host->port . '/' . $get('database'); - - $set('password', $newPassword); - $set('JDBC', $jdbcString); - } } diff --git a/app/Filament/Components/Forms/Actions/RotateDatabasePasswordAction.php b/app/Filament/Components/Forms/Actions/RotateDatabasePasswordAction.php new file mode 100644 index 0000000000..5c201081ec --- /dev/null +++ b/app/Filament/Components/Forms/Actions/RotateDatabasePasswordAction.php @@ -0,0 +1,62 @@ +label('Rotate'); + + $this->icon('tabler-refresh'); + + $this->authorize(fn (Database $database) => auth()->user()->can('update database', $database)); + + $this->modalHeading('Rotate Password'); + + $this->modalIconColor('warning'); + + $this->modalSubmitAction(fn (StaticAction $action) => $action->color('warning')); + + $this->requiresConfirmation(); + + $this->action(function (DatabasePasswordService $service, Database $database, Set $set) { + try { + $service->handle($database); + + $database->refresh(); + + $set('password', $database->password); + $set('jdbc', $database->jdbc); + + Notification::make() + ->title('Password rotated') + ->success() + ->send(); + } catch (Exception $exception) { + Notification::make() + ->title('Password rotation failed') + ->body($exception->getMessage()) + ->danger() + ->send(); + + report($exception); + } + }); + } +} diff --git a/app/Filament/Server/Resources/DatabaseResource/Pages/ListDatabases.php b/app/Filament/Server/Resources/DatabaseResource/Pages/ListDatabases.php index eae32e49b5..446d5831dc 100644 --- a/app/Filament/Server/Resources/DatabaseResource/Pages/ListDatabases.php +++ b/app/Filament/Server/Resources/DatabaseResource/Pages/ListDatabases.php @@ -2,22 +2,20 @@ namespace App\Filament\Server\Resources\DatabaseResource\Pages; +use App\Filament\Components\Forms\Actions\RotateDatabasePasswordAction; use App\Filament\Server\Resources\DatabaseResource; use App\Models\Database; use App\Models\DatabaseHost; use App\Models\Permission; use App\Models\Server; use App\Services\Databases\DatabaseManagementService; -use App\Services\Databases\DatabasePasswordService; use App\Tables\Columns\DateTimeColumn; use Filament\Actions\CreateAction; use Filament\Facades\Filament; -use Filament\Forms\Components\Actions\Action; use Filament\Forms\Components\Grid; use Filament\Forms\Components\Select; use Filament\Forms\Components\TextInput; use Filament\Forms\Form; -use Filament\Forms\Get; use Filament\Resources\Pages\ListRecords; use Filament\Tables\Actions\DeleteAction; use Filament\Tables\Actions\ViewAction; @@ -45,16 +43,8 @@ public function form(Form $form): Form ->password()->revealable() ->hidden(fn () => !auth()->user()->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server)) ->hintAction( - Action::make('rotate') + RotateDatabasePasswordAction::make() ->authorize(fn () => auth()->user()->can(Permission::ACTION_DATABASE_UPDATE, $server)) - ->icon('tabler-refresh') - ->requiresConfirmation() - ->action(function (DatabasePasswordService $service, Database $database, $set, $get) { - $newPassword = $service->handle($database); - - $set('password', $newPassword); - $set('JDBC', 'jdbc:mysql://' . $get('username') . ':' . urlencode($newPassword) . '@' . $database->host->host . ':' . $database->host->port . '/' . $get('database')); - }) ) ->suffixAction(CopyAction::make()) ->formatStateUsing(fn (Database $database) => $database->password), @@ -62,13 +52,13 @@ public function form(Form $form): Form ->label('Connections From'), TextInput::make('max_connections') ->formatStateUsing(fn (Database $database) => $database->max_connections === 0 ? $database->max_connections : 'Unlimited'), - TextInput::make('JDBC') + TextInput::make('jdbc') ->label('JDBC Connection String') ->password()->revealable() ->hidden(!auth()->user()->can(Permission::ACTION_DATABASE_VIEW_PASSWORD, $server)) ->suffixAction(CopyAction::make()) ->columnSpanFull() - ->formatStateUsing(fn (Get $get, Database $database) => 'jdbc:mysql://' . $get('username') . ':' . urlencode($database->password) . '@' . $database->host->host . ':' . $database->host->port . '/' . $get('database')), + ->formatStateUsing(fn (Database $database) => $database->jdbc), ]); } diff --git a/app/Models/Database.php b/app/Models/Database.php index 24e91ce961..795fa28aff 100644 --- a/app/Models/Database.php +++ b/app/Models/Database.php @@ -2,6 +2,7 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Facades\DB; @@ -14,6 +15,7 @@ * @property string $remote * @property string $password * @property int $max_connections + * @property string $jdbc * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at * @property \App\Models\Server $server @@ -87,6 +89,13 @@ public function server(): BelongsTo return $this->belongsTo(Server::class); } + protected function jdbc(): Attribute + { + return Attribute::make( + get: fn () => 'jdbc:mysql://' . $this->username . ':' . urlencode($this->password) . '@' . $this->host->host . ':' . $this->host->port . '/' . $this->database, + ); + } + /** * Run the provided statement against the database on a given connection. */ diff --git a/app/Services/Databases/DatabasePasswordService.php b/app/Services/Databases/DatabasePasswordService.php index 3677e71c1b..110c5abe5c 100644 --- a/app/Services/Databases/DatabasePasswordService.php +++ b/app/Services/Databases/DatabasePasswordService.php @@ -20,7 +20,7 @@ public function __construct( /** * Updates a password for a given database. */ - public function handle(Database|int $database): string + public function handle(Database|int $database): void { if (is_int($database)) { $database = Database::query()->findOrFail($database); @@ -40,7 +40,5 @@ public function handle(Database|int $database): string $database->assignUserToDatabase($database->database, $database->username, $database->remote); $database->flush(); }); - - return $password; } }