diff --git a/.env.example b/.env.example index eab42b85a..c91d26013 100644 --- a/.env.example +++ b/.env.example @@ -17,9 +17,6 @@ CACHE_STORE=file QUEUE_CONNECTION=database SESSION_DRIVER=file -HASHIDS_SALT= -HASHIDS_LENGTH=8 - MAIL_MAILER=log MAIL_HOST=smtp.example.com MAIL_PORT=25 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bcc56c1a7..998654549 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,7 +34,6 @@ jobs: MAIL_MAILER: array SESSION_DRIVER: array QUEUE_CONNECTION: sync - HASHIDS_SALT: alittlebitofsalt1234 DB_CONNECTION: mysql DB_HOST: 127.0.0.1 DB_DATABASE: testing @@ -97,7 +96,6 @@ jobs: MAIL_MAILER: array SESSION_DRIVER: array QUEUE_CONNECTION: sync - HASHIDS_SALT: alittlebitofsalt1234 DB_CONNECTION: sqlite DB_DATABASE: testing.sqlite steps: diff --git a/app/Console/Commands/Environment/AppSettingsCommand.php b/app/Console/Commands/Environment/AppSettingsCommand.php index 8a01437fa..ff75057a3 100644 --- a/app/Console/Commands/Environment/AppSettingsCommand.php +++ b/app/Console/Commands/Environment/AppSettingsCommand.php @@ -32,7 +32,6 @@ class AppSettingsCommand extends Command protected $description = 'Configure basic environment settings for the Panel.'; protected $signature = 'p:environment:setup - {--new-salt : Whether or not to generate a new salt for Hashids.} {--url= : The URL that this Panel is running on.} {--cache= : The cache driver backend to use.} {--session= : The session driver backend to use.} @@ -61,10 +60,6 @@ class AppSettingsCommand extends Command { $this->variables['APP_TIMEZONE'] = 'UTC'; - if (empty(config('hashids.salt')) || $this->option('new-salt')) { - $this->variables['HASHIDS_SALT'] = str_random(20); - } - $this->output->comment(__('commands.appsettings.comment.url')); $this->variables['APP_URL'] = $this->option('url') ?? $this->ask( 'Application URL', diff --git a/app/Console/Commands/Schedule/ProcessRunnableCommand.php b/app/Console/Commands/Schedule/ProcessRunnableCommand.php index 343328cc8..687752a2d 100644 --- a/app/Console/Commands/Schedule/ProcessRunnableCommand.php +++ b/app/Console/Commands/Schedule/ProcessRunnableCommand.php @@ -62,7 +62,7 @@ class ProcessRunnableCommand extends Command $this->line(trans('command/messages.schedule.output_line', [ 'schedule' => $schedule->name, - 'hash' => $schedule->hashid, + 'id' => $schedule->id, ])); } catch (\Throwable|\Exception $exception) { logger()->error($exception, ['schedule_id' => $schedule->id]); diff --git a/app/Contracts/Extensions/HashidsInterface.php b/app/Contracts/Extensions/HashidsInterface.php deleted file mode 100644 index 4f02d0bea..000000000 --- a/app/Contracts/Extensions/HashidsInterface.php +++ /dev/null @@ -1,15 +0,0 @@ -url())->contains('livewire')) { + if ($request->is('livewire/update')) { Notification::make() ->title(static::class) ->body($this->getMessage()) diff --git a/app/Extensions/DynamicDatabaseConnection.php b/app/Extensions/DynamicDatabaseConnection.php index 3a8ba68d2..d14407427 100644 --- a/app/Extensions/DynamicDatabaseConnection.php +++ b/app/Extensions/DynamicDatabaseConnection.php @@ -25,7 +25,7 @@ class DynamicDatabaseConnection 'port' => $host->port, 'database' => $database, 'username' => $host->username, - 'password' => decrypt($host->password), + 'password' => $host->password, 'charset' => self::DB_CHARSET, 'collation' => self::DB_COLLATION, ]); diff --git a/app/Extensions/Hashids.php b/app/Extensions/Hashids.php deleted file mode 100644 index 25ce002fc..000000000 --- a/app/Extensions/Hashids.php +++ /dev/null @@ -1,22 +0,0 @@ -decode($encoded); - if (!is_array($result)) { - return $default; - } - - return array_first($result, null, $default); - } -} diff --git a/app/Filament/Resources/ApiKeyResource.php b/app/Filament/Resources/ApiKeyResource.php index b4bea3352..0d7817cd9 100644 --- a/app/Filament/Resources/ApiKeyResource.php +++ b/app/Filament/Resources/ApiKeyResource.php @@ -4,9 +4,7 @@ namespace App\Filament\Resources; use App\Filament\Resources\ApiKeyResource\Pages; use App\Models\ApiKey; -use Filament\Resources\Components\Tab; use Filament\Resources\Resource; -use Illuminate\Database\Eloquent\Builder; class ApiKeyResource extends Resource { @@ -16,7 +14,7 @@ class ApiKeyResource extends Resource public static function getNavigationBadge(): ?string { - return static::getModel()::count() ?: null; + return static::getModel()::where('key_type', '2')->count() ?: null; } public static function canEdit($record): bool @@ -24,20 +22,6 @@ class ApiKeyResource extends Resource return false; } - public function getTabs(): array - { - return [ - 'all' => Tab::make('All Keys'), - 'application' => Tab::make('Application Keys') - ->modifyQueryUsing(fn (Builder $query) => $query->where('key_type', ApiKey::TYPE_APPLICATION)), - ]; - } - - public function getDefaultActiveTab(): string|int|null - { - return 'application'; - } - public static function getRelations(): array { return [ diff --git a/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php b/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php index e8abde957..64fd7c77c 100644 --- a/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php +++ b/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php @@ -19,30 +19,16 @@ class CreateApiKey extends CreateRecord return $form ->schema([ Forms\Components\Hidden::make('identifier')->default(ApiKey::generateTokenIdentifier(ApiKey::TYPE_APPLICATION)), - Forms\Components\Hidden::make('token')->default(encrypt(str_random(ApiKey::KEY_LENGTH))), + Forms\Components\Hidden::make('token')->default(str_random(ApiKey::KEY_LENGTH)), Forms\Components\Hidden::make('user_id') ->default(auth()->user()->id) ->required(), - Forms\Components\Select::make('key_type') + Forms\Components\Hidden::make('key_type') ->inlineLabel() - ->options(function (ApiKey $apiKey) { - $originalOptions = [ - //ApiKey::TYPE_NONE => 'None', - ApiKey::TYPE_ACCOUNT => 'Account', - ApiKey::TYPE_APPLICATION => 'Application', - //ApiKey::TYPE_DAEMON_USER => 'Daemon User', - //ApiKey::TYPE_DAEMON_APPLICATION => 'Daemon Application', - ]; - - return collect($originalOptions) - ->filter(fn ($value, $key) => $key <= ApiKey::TYPE_APPLICATION || $apiKey->key_type === $key) - ->all(); - }) - ->selectablePlaceholder(false) - ->required() - ->default(ApiKey::TYPE_APPLICATION), + ->default(ApiKey::TYPE_APPLICATION) + ->required(), Forms\Components\Fieldset::make('Permissions') ->columns([ diff --git a/app/Filament/Resources/ApiKeyResource/Pages/ListApiKeys.php b/app/Filament/Resources/ApiKeyResource/Pages/ListApiKeys.php index 0932848eb..e1f5ac436 100644 --- a/app/Filament/Resources/ApiKeyResource/Pages/ListApiKeys.php +++ b/app/Filament/Resources/ApiKeyResource/Pages/ListApiKeys.php @@ -5,10 +5,8 @@ namespace App\Filament\Resources\ApiKeyResource\Pages; use App\Filament\Resources\ApiKeyResource; use App\Models\ApiKey; use Filament\Actions; -use Filament\Resources\Components\Tab; use Filament\Resources\Pages\ListRecords; use Filament\Tables\Table; -use Illuminate\Database\Eloquent\Builder; use Filament\Tables; class ListApiKeys extends ListRecords @@ -19,16 +17,12 @@ class ListApiKeys extends ListRecords { return $table ->searchable(false) + ->modifyQueryUsing(fn ($query) => $query->where('key_type', ApiKey::TYPE_APPLICATION)) ->columns([ - Tables\Columns\TextColumn::make('user.username') - ->hidden() - ->searchable() - ->sortable(), - Tables\Columns\TextColumn::make('key') ->copyable() ->icon('tabler-clipboard-text') - ->state(fn (ApiKey $key) => $key->identifier . decrypt($key->token)), + ->state(fn (ApiKey $key) => $key->identifier . $key->token), Tables\Columns\TextColumn::make('memo') ->label('Description') @@ -41,6 +35,7 @@ class ListApiKeys extends ListRecords Tables\Columns\TextColumn::make('last_used_at') ->label('Last Used') + ->placeholder('Not Used') ->dateTime() ->sortable(), @@ -48,13 +43,13 @@ class ListApiKeys extends ListRecords ->label('Created') ->dateTime() ->sortable(), - ]) - ->filters([ - // + + Tables\Columns\TextColumn::make('user.username') + ->label('Created By') + ->url(fn (ApiKey $apiKey): string => route('filament.admin.resources.users.edit', ['record' => $apiKey->user])), ]) ->actions([ Tables\Actions\DeleteAction::make(), - //Tables\Actions\EditAction::make() ]); } @@ -64,22 +59,4 @@ class ListApiKeys extends ListRecords Actions\CreateAction::make(), ]; } - - public function getTabs(): array - { - return [ - 'all' => Tab::make('All Keys'), - 'application' => Tab::make('Application Keys') - ->modifyQueryUsing(fn (Builder $query) => $query->where('key_type', ApiKey::TYPE_APPLICATION) - ), - 'account' => Tab::make('Account Keys') - ->modifyQueryUsing(fn (Builder $query) => $query->where('key_type', ApiKey::TYPE_ACCOUNT) - ), - ]; - } - - public function getDefaultActiveTab(): string|int|null - { - return 'application'; - } } diff --git a/app/Filament/Resources/DatabaseHostResource/Pages/CreateDatabaseHost.php b/app/Filament/Resources/DatabaseHostResource/Pages/CreateDatabaseHost.php index 8280c95c6..dd37e5c26 100644 --- a/app/Filament/Resources/DatabaseHostResource/Pages/CreateDatabaseHost.php +++ b/app/Filament/Resources/DatabaseHostResource/Pages/CreateDatabaseHost.php @@ -74,15 +74,6 @@ class CreateDatabaseHost extends CreateRecord ]); } - protected function mutateFormDataBeforeCreate(array $data): array - { - if (isset($data['password'])) { - $data['password'] = encrypt($data['password']); - } - - return $data; - } - protected function getHeaderActions(): array { return [ diff --git a/app/Filament/Resources/DatabaseHostResource/Pages/EditDatabaseHost.php b/app/Filament/Resources/DatabaseHostResource/Pages/EditDatabaseHost.php index 4817796b4..a6084bfc7 100644 --- a/app/Filament/Resources/DatabaseHostResource/Pages/EditDatabaseHost.php +++ b/app/Filament/Resources/DatabaseHostResource/Pages/EditDatabaseHost.php @@ -76,15 +76,6 @@ class EditDatabaseHost extends EditRecord ]; } - protected function mutateFormDataBeforeSave(array $data): array - { - if (isset($data['password'])) { - $data['password'] = encrypt($data['password']); - } - - return $data; - } - protected function getFormActions(): array { return []; diff --git a/app/Filament/Resources/DatabaseHostResource/RelationManagers/DatabasesRelationManager.php b/app/Filament/Resources/DatabaseHostResource/RelationManagers/DatabasesRelationManager.php index bee230001..e3b622840 100644 --- a/app/Filament/Resources/DatabaseHostResource/RelationManagers/DatabasesRelationManager.php +++ b/app/Filament/Resources/DatabaseHostResource/RelationManagers/DatabasesRelationManager.php @@ -28,13 +28,13 @@ class DatabasesRelationManager extends RelationManager ->requiresConfirmation() ->action(fn (DatabasePasswordService $service, Database $database, $set, $get) => $this->rotatePassword($service, $database, $set, $get)) ) - ->formatStateUsing(fn (Database $database) => decrypt($database->password)), + ->formatStateUsing(fn (Database $database) => $database->password), Forms\Components\TextInput::make('remote')->label('Connections From'), Forms\Components\TextInput::make('max_connections'), Forms\Components\TextInput::make('JDBC') ->label('JDBC Connection String') ->columnSpanFull() - ->formatStateUsing(fn (Forms\Get $get, Database $database) => 'jdbc:mysql://' . $get('username') . ':' . urlencode(decrypt($database->password)) . '@' . $database->host->host . ':' . $database->host->port . '/' . $get('database')), + ->formatStateUsing(fn (Forms\Get $get, Database $database) => 'jdbc:mysql://' . $get('username') . ':' . urlencode($database->password) . '@' . $database->host->host . ':' . $database->host->port . '/' . $get('database')), ]); } public function table(Table $table): Table diff --git a/app/Filament/Resources/NodeResource/Pages/CreateNode.php b/app/Filament/Resources/NodeResource/Pages/CreateNode.php index 899e7b3ab..1d506083e 100644 --- a/app/Filament/Resources/NodeResource/Pages/CreateNode.php +++ b/app/Filament/Resources/NodeResource/Pages/CreateNode.php @@ -337,6 +337,7 @@ class CreateNode extends CreateRecord ->suffix('%') ->columnSpan(2) ->numeric() + ->default(0) ->minValue(0), Forms\Components\TextInput::make('cpu_overallocate') ->dehydratedWhenHidden() @@ -346,6 +347,7 @@ class CreateNode extends CreateRecord ->hintIconTooltip('The % allowable to go over the set limit.') ->columnSpan(2) ->numeric() + ->default(0) ->minValue(-1) ->maxValue(100) ->suffix('%'), diff --git a/app/Filament/Resources/NodeResource/Pages/EditNode.php b/app/Filament/Resources/NodeResource/Pages/EditNode.php index a5126f694..ca0ac657f 100644 --- a/app/Filament/Resources/NodeResource/Pages/EditNode.php +++ b/app/Filament/Resources/NodeResource/Pages/EditNode.php @@ -6,9 +6,11 @@ use App\Filament\Resources\NodeResource; use App\Filament\Resources\NodeResource\Widgets\NodeMemoryChart; use App\Filament\Resources\NodeResource\Widgets\NodeStorageChart; use App\Models\Node; +use App\Services\Nodes\NodeUpdateService; use Filament\Actions; use Filament\Forms; use Filament\Forms\Components\Tabs; +use Filament\Notifications\Notification; use Filament\Resources\Pages\EditRecord; use Illuminate\Support\HtmlString; use Webbingbrasil\FilamentCopyActions\Forms\Actions\CopyAction; @@ -374,6 +376,18 @@ class EditNode extends EditRecord ->rows(19) ->hintAction(CopyAction::make()) ->columnSpanFull(), + Forms\Components\Actions::make([ + Forms\Components\Actions\Action::make('resetKey') + ->label('Reset Daemon Token') + ->color('danger') + ->requiresConfirmation() + ->modalHeading('Reset Daemon Token?') + ->modalDescription('Resetting the daemon token will void any request coming from the old token. This token is used for all sensitive operations on the daemon including server creation and deletion. We suggest changing this token regularly for security.') + ->action(fn (NodeUpdateService $nodeUpdateService, Node $node) => $nodeUpdateService->handle($node, [], true) + && Notification::make()->success()->title('Daemon Key Reset')->send() + && $this->fillForm() + ), + ]), ]), ]), ]); 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 7fe5728cf..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.'), ]; }), @@ -158,7 +186,7 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile ->schema([ Grid::make('asdf')->columns(5)->schema([ Section::make('Create API Key')->columnSpan(3)->schema([ - TextInput::make('description'), + TextInput::make('description')->required(), TagsInput::make('allowed_ips') ->splitKeys([',', ' ', 'Tab']) ->placeholder('Example: 127.0.0.1 or 192.168.1.1') @@ -182,8 +210,9 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile $action->success(); }), ]), - Section::make('API Keys')->columnSpan(2)->schema([ + Section::make('Keys')->columnSpan(2)->schema([ Repeater::make('keys') + ->label('') ->relationship('apiKeys') ->addable(false) ->itemLabel(fn ($state) => $state['identifier']) @@ -235,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/Http/Controllers/Admin/NodeAutoDeployController.php b/app/Http/Controllers/Admin/NodeAutoDeployController.php index 95221d33f..1029706c3 100644 --- a/app/Http/Controllers/Admin/NodeAutoDeployController.php +++ b/app/Http/Controllers/Admin/NodeAutoDeployController.php @@ -56,7 +56,7 @@ class NodeAutoDeployController extends Controller return new JsonResponse([ 'node' => $node->id, - 'token' => $key->identifier . decrypt($key->token), + 'token' => $key->identifier . $key->token, ]); } } diff --git a/app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php b/app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php index f00c8d667..7a16fab01 100644 --- a/app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php +++ b/app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php @@ -24,8 +24,8 @@ class NodeDeploymentController extends ApplicationApiController $data = $request->validated(); $nodes = $this->viableNodesService->handle( - $data['disk'] ?? 0, $data['memory'] ?? 0, + $data['disk'] ?? 0, $data['cpu'] ?? 0, $data['tags'] ?? $data['location_ids'] ?? [], ); diff --git a/app/Http/Controllers/Auth/LoginCheckpointController.php b/app/Http/Controllers/Auth/LoginCheckpointController.php index f022d359d..540aca5a0 100644 --- a/app/Http/Controllers/Auth/LoginCheckpointController.php +++ b/app/Http/Controllers/Auth/LoginCheckpointController.php @@ -65,9 +65,7 @@ class LoginCheckpointController extends AbstractLoginController return $this->sendLoginResponse($user, $request); } } else { - $decrypted = decrypt($user->totp_secret); - - if ($this->google2FA->verifyKey($decrypted, (string) $request->input('authentication_code'), config('panel.auth.2fa.window'))) { + if ($this->google2FA->verifyKey($user->totp_secret, (string) $request->input('authentication_code'), config('panel.auth.2fa.window'))) { Event::dispatch(new ProvidedAuthenticationToken($user)); return $this->sendLoginResponse($user, $request); diff --git a/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php b/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php index ce40796b8..5bacb25b6 100644 --- a/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php +++ b/app/Http/Middleware/Api/Daemon/DaemonAuthenticate.php @@ -41,7 +41,7 @@ class DaemonAuthenticate /** @var Node $node */ $node = Node::query()->where('daemon_token_id', $parts[0])->firstOrFail(); - if (hash_equals((string) decrypt($node->daemon_token), $parts[1])) { + if (hash_equals((string) $node->daemon_token, $parts[1])) { $request->attributes->set('node', $node); return $next($request); diff --git a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php index fd735edf0..263b7c540 100644 --- a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php +++ b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php @@ -56,11 +56,10 @@ class StoreServerRequest extends ApplicationApiRequest // Automatic deployment rules 'deploy' => 'sometimes|required|array', 'deploy.locations' => 'array', - 'deploy.locations.*' => 'integer|min:1', + 'deploy.locations.*' => 'required_with:deploy.locations,integer|min:1', 'deploy.dedicated_ip' => 'required_with:deploy,boolean', 'deploy.port_range' => 'array', 'deploy.port_range.*' => 'string', - 'start_on_completion' => 'sometimes|boolean', ]; } diff --git a/app/Models/Allocation.php b/app/Models/Allocation.php index 6a7d45db7..33bc7f16d 100644 --- a/app/Models/Allocation.php +++ b/app/Models/Allocation.php @@ -22,7 +22,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; * @property bool $has_alias * @property \App\Models\Server|null $server * @property \App\Models\Node $node - * @property string $hashid * * @method static \Database\Factories\AllocationFactory factory(...$parameters) * @method static \Illuminate\Database\Eloquent\Builder|Allocation newModelQuery() @@ -88,14 +87,6 @@ class Allocation extends Model return $this->getKeyName(); } - /** - * Return a hashid encoded string to represent the ID of the allocation. - */ - public function getHashidAttribute(): string - { - return app()->make('hashids')->encode($this->id); - } - /** * Accessor to automatically provide the IP alias if defined. */ diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php index 629212526..21c044c54 100644 --- a/app/Models/ApiKey.php +++ b/app/Models/ApiKey.php @@ -149,6 +149,7 @@ class ApiKey extends Model 'user_id' => 'int', 'last_used_at' => 'datetime', 'expires_at' => 'datetime', + 'token' => 'encrypted', self::CREATED_AT => 'datetime', self::UPDATED_AT => 'datetime', 'r_' . AdminAcl::RESOURCE_USERS => 'int', @@ -188,7 +189,7 @@ class ApiKey extends Model $identifier = substr($token, 0, self::IDENTIFIER_LENGTH); $model = static::where('identifier', $identifier)->first(); - if (!is_null($model) && decrypt($model->token) === substr($token, strlen($identifier))) { + if (!is_null($model) && $model->token === substr($token, strlen($identifier))) { return $model; } diff --git a/app/Models/Database.php b/app/Models/Database.php index 6d4fde2f9..bd6bc6de3 100644 --- a/app/Models/Database.php +++ b/app/Models/Database.php @@ -2,9 +2,7 @@ namespace App\Models; -use Illuminate\Container\Container; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use App\Contracts\Extensions\HashidsInterface; use Illuminate\Support\Facades\DB; /** @@ -64,6 +62,7 @@ class Database extends Model 'server_id' => 'integer', 'database_host_id' => 'integer', 'max_connections' => 'integer', + 'password' => 'encrypted', ]; } @@ -72,26 +71,6 @@ class Database extends Model return $this->getKeyName(); } - /** - * Resolves the database using the ID by checking if the value provided is a HashID - * string value, or just the ID to the database itself. - * - * @param mixed $value - * @param string|null $field - * - * @throws \Illuminate\Contracts\Container\BindingResolutionException - */ - public function resolveRouteBinding($value, $field = null): ?\Illuminate\Database\Eloquent\Model - { - if (is_scalar($value) && ($field ?? $this->getRouteKeyName()) === 'id') { - $value = ctype_digit((string) $value) - ? $value - : Container::getInstance()->make(HashidsInterface::class)->decodeFirst($value); - } - - return $this->where($field ?? $this->getRouteKeyName(), $value)->firstOrFail(); - } - /** * Gets the host database server associated with a database. */ diff --git a/app/Models/DatabaseHost.php b/app/Models/DatabaseHost.php index e06c3b957..159e17261 100644 --- a/app/Models/DatabaseHost.php +++ b/app/Models/DatabaseHost.php @@ -60,6 +60,7 @@ class DatabaseHost extends Model 'id' => 'integer', 'max_databases' => 'integer', 'node_id' => 'integer', + 'password' => 'encrypted', 'created_at' => 'immutable_datetime', 'updated_at' => 'immutable_datetime', ]; diff --git a/app/Models/Node.php b/app/Models/Node.php index c693c5b0d..65aeaa0ea 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -63,10 +63,6 @@ class Node extends Model */ protected $hidden = ['daemon_token_id', 'daemon_token']; - public int $sum_memory; - public int $sum_disk; - public int $sum_cpu; - /** * Fields that are mass assignable. */ @@ -127,6 +123,7 @@ class Node extends Model 'cpu' => 'integer', 'daemon_listen' => 'integer', 'daemon_sftp' => 'integer', + 'daemon_token' => 'encrypted', 'behind_proxy' => 'boolean', 'public' => 'boolean', 'maintenance_mode' => 'boolean', @@ -143,7 +140,7 @@ class Node extends Model { static::creating(function (self $node) { $node->uuid = Str::uuid(); - $node->daemon_token = encrypt(Str::random(self::DAEMON_TOKEN_LENGTH)); + $node->daemon_token = Str::random(self::DAEMON_TOKEN_LENGTH); $node->daemon_token_id = Str::random(self::DAEMON_TOKEN_ID_LENGTH); return true; @@ -171,7 +168,7 @@ class Node extends Model 'debug' => false, 'uuid' => $this->uuid, 'token_id' => $this->daemon_token_id, - 'token' => decrypt($this->daemon_token), + 'token' => $this->daemon_token, 'api' => [ 'host' => '0.0.0.0', 'port' => $this->daemon_listen, @@ -209,16 +206,6 @@ class Node extends Model return json_encode($this->getConfiguration(), $pretty ? JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT : JSON_UNESCAPED_SLASHES); } - /** - * Helper function to return the decrypted key for a node. - */ - public function getDecryptedKey(): string - { - return (string) decrypt( - $this->daemon_token - ); - } - public function isUnderMaintenance(): bool { return $this->maintenance_mode; @@ -250,11 +237,28 @@ class Node extends Model */ public function isViable(int $memory, int $disk, int $cpu): bool { - $memoryLimit = $this->memory * (1 + ($this->memory_overallocate / 100)); - $diskLimit = $this->disk * (1 + ($this->disk_overallocate / 100)); - $cpuLimit = $this->cpu * (1 + ($this->cpu_overallocate / 100)); + if ($this->memory_overallocate >= 0) { + $memoryLimit = $this->memory * (1 + ($this->memory_overallocate / 100)); + if ($this->servers_sum_memory + $memory > $memoryLimit) { + return false; + } + } - return ($this->sum_memory + $memory) <= $memoryLimit && ($this->sum_disk + $disk) <= $diskLimit && ($this->sum_cpu + $cpu) <= $cpuLimit; + if ($this->disk_overallocate >= 0) { + $diskLimit = $this->disk * (1 + ($this->disk_overallocate / 100)); + if ($this->servers_sum_disk + $disk > $diskLimit) { + return false; + } + } + + if ($this->cpu_overallocate >= 0) { + $cpuLimit = $this->cpu * (1 + ($this->cpu_overallocate / 100)); + if ($this->servers_sum_cpu + $cpu > $cpuLimit) { + return false; + } + } + + return true; } public static function getForServerCreation() diff --git a/app/Models/Schedule.php b/app/Models/Schedule.php index 8fb3f4167..7042d25a4 100644 --- a/app/Models/Schedule.php +++ b/app/Models/Schedule.php @@ -4,10 +4,8 @@ namespace App\Models; use Cron\CronExpression; use Carbon\CarbonImmutable; -use Illuminate\Container\Container; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use App\Contracts\Extensions\HashidsInterface; /** * @property int $id @@ -25,7 +23,6 @@ use App\Contracts\Extensions\HashidsInterface; * @property \Carbon\Carbon|null $next_run_at * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at - * @property string $hashid * @property \App\Models\Server $server * @property \App\Models\Task[]|\Illuminate\Support\Collection $tasks */ @@ -124,14 +121,6 @@ class Schedule extends Model ); } - /** - * Return a hashid encoded string to represent the ID of the schedule. - */ - public function getHashidAttribute(): string - { - return Container::getInstance()->make(HashidsInterface::class)->encode($this->id); - } - /** * Return tasks belonging to a schedule. */ diff --git a/app/Models/Subuser.php b/app/Models/Subuser.php index 0c1f4d382..566c8a741 100644 --- a/app/Models/Subuser.php +++ b/app/Models/Subuser.php @@ -52,14 +52,6 @@ class Subuser extends Model ]; } - /** - * Return a hashid encoded string to represent the ID of the subuser. - */ - public function getHashidAttribute(): string - { - return app()->make('hashids')->encode($this->id); - } - /** * Gets the server associated with a subuser. */ diff --git a/app/Models/Task.php b/app/Models/Task.php index 591bfcfbb..6545ab5f5 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -2,10 +2,8 @@ namespace App\Models; -use Illuminate\Container\Container; use Illuminate\Database\Eloquent\Relations\HasOneThrough; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use App\Contracts\Extensions\HashidsInterface; /** * @property int $id @@ -18,7 +16,6 @@ use App\Contracts\Extensions\HashidsInterface; * @property bool $continue_on_failure * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at - * @property string $hashid * @property \App\Models\Schedule $schedule * @property \App\Models\Server $server */ @@ -96,14 +93,6 @@ class Task extends Model return $this->getKeyName(); } - /** - * Return a hashid encoded string to represent the ID of the task. - */ - public function getHashidAttribute(): string - { - return Container::getInstance()->make(HashidsInterface::class)->encode($this->id); - } - /** * Return the schedule that a task belongs to. */ diff --git a/app/Models/Traits/HasAccessTokens.php b/app/Models/Traits/HasAccessTokens.php index f6acac458..14a0f685b 100644 --- a/app/Models/Traits/HasAccessTokens.php +++ b/app/Models/Traits/HasAccessTokens.php @@ -31,7 +31,7 @@ trait HasAccessTokens 'user_id' => $this->id, 'key_type' => ApiKey::TYPE_ACCOUNT, 'identifier' => ApiKey::generateTokenIdentifier(ApiKey::TYPE_ACCOUNT), - 'token' => encrypt($plain = Str::random(ApiKey::KEY_LENGTH)), + 'token' => $plain = Str::random(ApiKey::KEY_LENGTH), 'memo' => $memo ?? '', 'allowed_ips' => $ips ?? [], ]); diff --git a/app/Models/User.php b/app/Models/User.php index b50dba9f0..f089aba07 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -171,6 +171,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac 'use_totp' => 'boolean', 'gravatar' => 'boolean', 'totp_authenticated_at' => 'datetime', + 'totp_secret' => 'encrypted', ]; } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 6317b6213..1c0deb622 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -60,7 +60,7 @@ class AppServiceProvider extends ServiceProvider 'daemon', fn (Node $node, array $headers = []) => Http::acceptJson() ->asJson() - ->withToken($node->getDecryptedKey()) + ->withToken($node->daemon_token) ->withHeaders($headers) ->withOptions(['verify' => (bool) app()->environment('production')]) ->timeout(config('panel.guzzle.timeout')) diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index 575fa500b..60339c744 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -35,11 +35,13 @@ class AdminPanelProvider extends PanelProvider ->default() ->id('admin') ->path('admin') - ->topNavigation(config('panel.filament.top-navigation', false)) + ->topNavigation(config('panel.filament.top-navigation', true)) ->login() ->homeUrl('/') - ->favicon('/pelican.ico') - ->brandName('Pelican') + ->favicon(config('app.favicon', '/pelican.ico')) + ->brandName(config('app.name', 'Pelican')) + ->brandLogo(config('app.logo')) + ->brandLogoHeight('2rem') ->profile(EditProfile::class, false) ->colors([ 'danger' => Color::Red, diff --git a/app/Providers/HashidsServiceProvider.php b/app/Providers/HashidsServiceProvider.php deleted file mode 100644 index 4639bfaed..000000000 --- a/app/Providers/HashidsServiceProvider.php +++ /dev/null @@ -1,26 +0,0 @@ -app->singleton(HashidsInterface::class, function () { - return new Hashids( - config('hashids.salt', ''), - config('hashids.length', 0), - config('hashids.alphabet', 'abcdefghijkmlnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890') - ); - }); - - $this->app->alias(HashidsInterface::class, 'hashids'); - } -} diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 449a083c5..432ededc0 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -3,7 +3,6 @@ namespace App\Providers; use Illuminate\Http\Request; -use App\Models\Database; use Illuminate\Foundation\Http\Middleware\TrimStrings; use Illuminate\Support\Facades\Route; use Illuminate\Cache\RateLimiting\Limit; @@ -29,11 +28,6 @@ class RouteServiceProvider extends ServiceProvider return preg_match(self::FILE_PATH_REGEX, $request->getPathInfo()) === 1; }); - // This is needed to make use of the "resolveRouteBinding" functionality in the - // model. Without it you'll never trigger that logic flow thus resulting in a 404 - // error because we request databases with a HashID, and not with a normal ID. - Route::model('database', Database::class); - $this->routes(function () { Route::middleware('web')->group(function () { Route::middleware(['auth.session', RequireTwoFactorAuthentication::class]) diff --git a/app/Services/Api/KeyCreationService.php b/app/Services/Api/KeyCreationService.php index 521702d4e..7fde94ba2 100644 --- a/app/Services/Api/KeyCreationService.php +++ b/app/Services/Api/KeyCreationService.php @@ -31,7 +31,7 @@ class KeyCreationService $data = array_merge($data, [ 'key_type' => $this->keyType, 'identifier' => ApiKey::generateTokenIdentifier($this->keyType), - 'token' => encrypt(str_random(ApiKey::KEY_LENGTH)), + 'token' => str_random(ApiKey::KEY_LENGTH), ]); if ($this->keyType === ApiKey::TYPE_APPLICATION) { diff --git a/app/Services/Databases/DatabaseManagementService.php b/app/Services/Databases/DatabaseManagementService.php index 9e5f8cf5a..364e89c6a 100644 --- a/app/Services/Databases/DatabaseManagementService.php +++ b/app/Services/Databases/DatabaseManagementService.php @@ -86,9 +86,7 @@ class DatabaseManagementService $data = array_merge($data, [ 'server_id' => $server->id, 'username' => sprintf('u%d_%s', $server->id, str_random(10)), - 'password' => encrypt( - Utilities::randomStringWithSpecialCharacters(24) - ), + 'password' => Utilities::randomStringWithSpecialCharacters(24), ]); return $this->connection->transaction(function () use ($data, &$database) { @@ -100,7 +98,7 @@ class DatabaseManagementService $database->createUser( $database->username, $database->remote, - decrypt($database->password), + $database->password, $database->max_connections ); $database->assignUserToDatabase($database->database, $database->username, $database->remote); diff --git a/app/Services/Databases/DatabasePasswordService.php b/app/Services/Databases/DatabasePasswordService.php index 71bae762a..47a8162f8 100644 --- a/app/Services/Databases/DatabasePasswordService.php +++ b/app/Services/Databases/DatabasePasswordService.php @@ -33,7 +33,7 @@ class DatabasePasswordService $this->dynamic->set('dynamic', $database->database_host_id); $database->update([ - 'password' => encrypt($password), + 'password' => $password, ]); $database->dropUser($database->username, $database->remote); diff --git a/app/Services/Databases/Hosts/HostCreationService.php b/app/Services/Databases/Hosts/HostCreationService.php index f18568949..dd9479aec 100644 --- a/app/Services/Databases/Hosts/HostCreationService.php +++ b/app/Services/Databases/Hosts/HostCreationService.php @@ -28,7 +28,7 @@ class HostCreationService { return $this->connection->transaction(function () use ($data) { $host = DatabaseHost::query()->create([ - 'password' => encrypt(array_get($data, 'password')), + 'password' => array_get($data, 'password'), 'name' => array_get($data, 'name'), 'host' => array_get($data, 'host'), 'port' => array_get($data, 'port'), diff --git a/app/Services/Databases/Hosts/HostUpdateService.php b/app/Services/Databases/Hosts/HostUpdateService.php index 16f55dddd..d4975cae0 100644 --- a/app/Services/Databases/Hosts/HostUpdateService.php +++ b/app/Services/Databases/Hosts/HostUpdateService.php @@ -26,9 +26,7 @@ class HostUpdateService */ public function handle(int $hostId, array $data): DatabaseHost { - if (!empty(array_get($data, 'password'))) { - $data['password'] = encrypt($data['password']); - } else { + if (empty(array_get($data, 'password'))) { unset($data['password']); } diff --git a/app/Services/Deployment/FindViableNodesService.php b/app/Services/Deployment/FindViableNodesService.php index 9a51d1c6f..4cffc71d2 100644 --- a/app/Services/Deployment/FindViableNodesService.php +++ b/app/Services/Deployment/FindViableNodesService.php @@ -17,19 +17,17 @@ class FindViableNodesService * are tossed out, as are any nodes marked as non-public, meaning automatic * deployments should not be done against them. */ - public function handle(int $disk = 0, int $memory = 0, int $cpu = 0, $tags = []): Collection + public function handle(int $memory = 0, int $disk = 0, int $cpu = 0, $tags = []): Collection { $nodes = Node::query() - ->withSum('servers', 'disk') ->withSum('servers', 'memory') + ->withSum('servers', 'disk') ->withSum('servers', 'cpu') ->where('public', true) ->get(); return $nodes ->filter(fn (Node $node) => !$tags || collect($node->tags)->intersect($tags)) - ->filter(fn (Node $node) => $node->servers_sum_disk + $disk <= $node->disk * (1 + $node->disk_overallocate / 100)) - ->filter(fn (Node $node) => $node->servers_sum_memory + $memory <= $node->memory * (1 + $node->memory_overallocate / 100)) - ->filter(fn (Node $node) => $node->servers_sum_cpu + $cpu <= $node->cpu * (1 + $node->cpu_overallocate / 100)); + ->filter(fn (Node $node) => $node->isViable($memory, $disk, $cpu)); } } diff --git a/app/Services/Eggs/Sharing/EggExporterService.php b/app/Services/Eggs/Sharing/EggExporterService.php index e3a561db2..19c9a5e12 100644 --- a/app/Services/Eggs/Sharing/EggExporterService.php +++ b/app/Services/Eggs/Sharing/EggExporterService.php @@ -25,6 +25,7 @@ class EggExporterService 'exported_at' => Carbon::now()->toAtomString(), 'name' => $egg->name, 'author' => $egg->author, + 'uuid' => $egg->uuid, 'description' => $egg->description, 'features' => $egg->features, 'docker_images' => $egg->docker_images, diff --git a/app/Services/Eggs/Sharing/EggImporterService.php b/app/Services/Eggs/Sharing/EggImporterService.php index 0a949474f..7ed7f010a 100644 --- a/app/Services/Eggs/Sharing/EggImporterService.php +++ b/app/Services/Eggs/Sharing/EggImporterService.php @@ -26,8 +26,11 @@ class EggImporterService $parsed = $this->parser->handle($file); return $this->connection->transaction(function () use ($parsed) { - $egg = (new Egg())->forceFill([ - 'uuid' => Uuid::uuid4()->toString(), + $uuid = $parsed['uuid'] ?? Uuid::uuid4()->toString(); + $egg = Egg::where('uuid', $uuid)->first() ?? new Egg(); + + $egg = $egg->forceFill([ + 'uuid' => $uuid, 'author' => Arr::get($parsed, 'author'), 'copy_script_from' => null, ]); diff --git a/app/Services/Nodes/NodeCreationService.php b/app/Services/Nodes/NodeCreationService.php index 0c7720d33..1c10b328d 100644 --- a/app/Services/Nodes/NodeCreationService.php +++ b/app/Services/Nodes/NodeCreationService.php @@ -16,7 +16,7 @@ class NodeCreationService public function handle(array $data): Node { $data['uuid'] = Uuid::uuid4()->toString(); - $data['daemon_token'] = encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH)); + $data['daemon_token'] = Str::random(Node::DAEMON_TOKEN_LENGTH); $data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH); return Node::query()->create($data); diff --git a/app/Services/Nodes/NodeJWTService.php b/app/Services/Nodes/NodeJWTService.php index 0221c5236..325fe04c0 100644 --- a/app/Services/Nodes/NodeJWTService.php +++ b/app/Services/Nodes/NodeJWTService.php @@ -63,7 +63,7 @@ class NodeJWTService public function handle(Node $node, ?string $identifiedBy, string $algo = 'md5'): Plain { $identifier = hash($algo, $identifiedBy); - $config = Configuration::forSymmetricSigner(new Sha256(), InMemory::plainText($node->getDecryptedKey())); + $config = Configuration::forSymmetricSigner(new Sha256(), InMemory::plainText($node->daemon_token)); $builder = $config->builder(new TimestampDates()) ->issuedBy(config('app.url')) diff --git a/app/Services/Nodes/NodeUpdateService.php b/app/Services/Nodes/NodeUpdateService.php index 76d1aadbd..f948686ed 100644 --- a/app/Services/Nodes/NodeUpdateService.php +++ b/app/Services/Nodes/NodeUpdateService.php @@ -28,14 +28,14 @@ class NodeUpdateService public function handle(Node $node, array $data, bool $resetToken = false): Node { if ($resetToken) { - $data['daemon_token'] = encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH)); + $data['daemon_token'] = Str::random(Node::DAEMON_TOKEN_LENGTH); $data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH); } [$updated, $exception] = $this->connection->transaction(function () use ($data, $node) { /** @var \App\Models\Node $updated */ - $updated = $node->replicate()->forceFill($data)->save(); - + $updated = $node->replicate(); + $updated->forceFill($data)->save(); try { // If we're changing the FQDN for the node, use the newly provided FQDN for the connection // address. This should alleviate issues where the node gets pointed to a "valid" FQDN that diff --git a/app/Services/Servers/ServerCreationService.php b/app/Services/Servers/ServerCreationService.php index 8953b1325..d555f7876 100644 --- a/app/Services/Servers/ServerCreationService.php +++ b/app/Services/Servers/ServerCreationService.php @@ -109,8 +109,8 @@ class ServerCreationService { /** @var Collection<\App\Models\Node> $nodes */ $nodes = $this->findViableNodesService->handle( - Arr::get($data, 'disk', 0), Arr::get($data, 'memory', 0), + Arr::get($data, 'disk', 0), Arr::get($data, 'cpu', 0), Arr::get($data, 'tags', []), ); @@ -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'), ]); } diff --git a/app/Services/Servers/TransferServerService.php b/app/Services/Servers/TransferServerService.php index 18f05dacf..4312369dd 100644 --- a/app/Services/Servers/TransferServerService.php +++ b/app/Services/Servers/TransferServerService.php @@ -58,7 +58,9 @@ class TransferServerService // Check if the node is viable for the transfer. $node = Node::query() ->select(['nodes.id', 'nodes.fqdn', 'nodes.scheme', 'nodes.daemon_token', 'nodes.daemon_listen', 'nodes.memory', 'nodes.disk', 'nodes.cpu', 'nodes.memory_overallocate', 'nodes.disk_overallocate', 'nodes.cpu_overallocate']) - ->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk, IFNULL(SUM(servers.cpu), 0) as sum_cpu') + ->withSum('servers', 'disk') + ->withSum('servers', 'memory') + ->withSum('servers', 'cpu') ->leftJoin('servers', 'servers.node_id', '=', 'nodes.id') ->where('nodes.id', $node_id) ->first(); diff --git a/app/Services/Users/ToggleTwoFactorService.php b/app/Services/Users/ToggleTwoFactorService.php index 321643a35..64518e39b 100644 --- a/app/Services/Users/ToggleTwoFactorService.php +++ b/app/Services/Users/ToggleTwoFactorService.php @@ -32,9 +32,7 @@ class ToggleTwoFactorService */ public function handle(User $user, string $token, bool $toggleState = null): array { - $secret = decrypt($user->totp_secret); - - $isValidToken = $this->google2FA->verifyKey($secret, $token, config()->get('panel.auth.2fa.window')); + $isValidToken = $this->google2FA->verifyKey($user->totp_secret, $token, config()->get('panel.auth.2fa.window')); if (!$isValidToken) { throw new TwoFactorAuthenticationTokenInvalid(); diff --git a/app/Services/Users/TwoFactorSetupService.php b/app/Services/Users/TwoFactorSetupService.php index dd1671d64..fb906c9db 100644 --- a/app/Services/Users/TwoFactorSetupService.php +++ b/app/Services/Users/TwoFactorSetupService.php @@ -26,7 +26,7 @@ class TwoFactorSetupService throw new \RuntimeException($exception->getMessage(), 0, $exception); } - $user->totp_secret = encrypt($secret); + $user->totp_secret = $secret; $user->save(); $company = urlencode(preg_replace('/\s/', '', config('app.name'))); diff --git a/app/Transformers/Api/Application/ServerDatabaseTransformer.php b/app/Transformers/Api/Application/ServerDatabaseTransformer.php index 1091dc867..e74cd2586 100644 --- a/app/Transformers/Api/Application/ServerDatabaseTransformer.php +++ b/app/Transformers/Api/Application/ServerDatabaseTransformer.php @@ -45,7 +45,7 @@ class ServerDatabaseTransformer extends BaseTransformer { return $this->item($model, function (Database $model) { return [ - 'password' => decrypt($model->password), + 'password' => $model->password, ]; }, 'database_password'); } diff --git a/app/Transformers/Api/Client/ActivityLogTransformer.php b/app/Transformers/Api/Client/ActivityLogTransformer.php index af666beb6..d02f95988 100644 --- a/app/Transformers/Api/Client/ActivityLogTransformer.php +++ b/app/Transformers/Api/Client/ActivityLogTransformer.php @@ -55,7 +55,7 @@ class ActivityLogTransformer extends BaseClientTransformer $properties = $model->properties ->mapWithKeys(function ($value, $key) use ($model) { - if ($key === 'ip' && !$model->actor->is($this->request->user())) { + if ($key === 'ip' && $model->actor && !$model->actor->is($this->request->user())) { return [$key => '[hidden]']; } diff --git a/app/Transformers/Api/Client/DatabaseTransformer.php b/app/Transformers/Api/Client/DatabaseTransformer.php index 2739ac7ec..1a3855939 100644 --- a/app/Transformers/Api/Client/DatabaseTransformer.php +++ b/app/Transformers/Api/Client/DatabaseTransformer.php @@ -6,22 +6,11 @@ use App\Models\Database; use League\Fractal\Resource\Item; use App\Models\Permission; use League\Fractal\Resource\NullResource; -use App\Contracts\Extensions\HashidsInterface; class DatabaseTransformer extends BaseClientTransformer { protected array $availableIncludes = ['password']; - private HashidsInterface $hashids; - - /** - * Handle dependency injection. - */ - public function handle(HashidsInterface $hashids) - { - $this->hashids = $hashids; - } - public function getResourceName(): string { return Database::RESOURCE_NAME; @@ -32,7 +21,7 @@ class DatabaseTransformer extends BaseClientTransformer $model->loadMissing('host'); return [ - 'id' => $this->hashids->encode($model->id), + 'id' => $model->id, 'host' => [ 'address' => $model->getRelation('host')->host, 'port' => $model->getRelation('host')->port, @@ -55,7 +44,7 @@ class DatabaseTransformer extends BaseClientTransformer return $this->item($database, function (Database $model) { return [ - 'password' => decrypt($model->password), + 'password' => $model->password, ]; }, 'database_password'); } diff --git a/bootstrap/providers.php b/bootstrap/providers.php index 04b20a98c..80be25c8e 100644 --- a/bootstrap/providers.php +++ b/bootstrap/providers.php @@ -6,7 +6,6 @@ return [ App\Providers\BackupsServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\Filament\AdminPanelProvider::class, - App\Providers\HashidsServiceProvider::class, App\Providers\RouteServiceProvider::class, App\Providers\ViewComposerServiceProvider::class, ]; diff --git a/composer.json b/composer.json index 29ef59290..407c79890 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,6 @@ "doctrine/dbal": "~3.6.0", "filament/filament": "^3.2", "guzzlehttp/guzzle": "^7.8.1", - "hashids/hashids": "~5.0.0", "laracasts/utilities": "~3.2.2", "laravel/framework": "^11.7", "laravel/helpers": "^1.7", @@ -34,7 +33,8 @@ "s1lentium/iptools": "~1.2.0", "spatie/laravel-fractal": "^6.2", "spatie/laravel-query-builder": "^5.8.1", - "symfony/mailgun-mailer": "^7.0.7", + "symfony/http-client": "^7.1", + "symfony/mailgun-mailer": "^7.1", "symfony/postmark-mailer": "^7.0.7", "symfony/yaml": "^7.0.7", "webbingbrasil/filament-copyactions": "^3.0.1", diff --git a/composer.lock b/composer.lock index 7a8205f17..d92e3bd6f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dc1c1e5ee766f2e31e84c50670fa0c98", + "content-hash": "328bdd29cb83793ec0a99b2210941740", "packages": [ { "name": "abdelhamiderrahmouni/filament-monaco-editor", @@ -2613,75 +2613,6 @@ ], "time": "2023-12-03T19:50:20+00:00" }, - { - "name": "hashids/hashids", - "version": "5.0.2", - "source": { - "type": "git", - "url": "https://github.com/vinkla/hashids.git", - "reference": "197171016b77ddf14e259e186559152eb3f8cf33" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/vinkla/hashids/zipball/197171016b77ddf14e259e186559152eb3f8cf33", - "reference": "197171016b77ddf14e259e186559152eb3f8cf33", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": "^8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "suggest": { - "ext-bcmath": "Required to use BC Math arbitrary precision mathematics (*).", - "ext-gmp": "Required to use GNU multiple precision mathematics (*)." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "psr-4": { - "Hashids\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ivan Akimov", - "email": "ivan@barreleye.com" - }, - { - "name": "Vincent Klaiber", - "email": "hello@doubledip.se" - } - ], - "description": "Generate short, unique, non-sequential ids (like YouTube and Bitly) from numbers", - "homepage": "https://hashids.org/php", - "keywords": [ - "bitly", - "decode", - "encode", - "hash", - "hashid", - "hashids", - "ids", - "obfuscate", - "youtube" - ], - "support": { - "issues": "https://github.com/vinkla/hashids/issues", - "source": "https://github.com/vinkla/hashids/tree/5.0.2" - }, - "time": "2023-02-23T15:00:54+00:00" - }, { "name": "kirschbaum-development/eloquent-power-joins", "version": "3.5.6", @@ -7722,6 +7653,178 @@ ], "time": "2024-04-18T09:29:19+00:00" }, + { + "name": "symfony/http-client", + "version": "v7.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "2266f9813ed7d8c84e04627edead7b7fd249d6e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/2266f9813ed7d8c84e04627edead7b7fd249d6e9", + "reference": "2266f9813ed7d8c84e04627edead7b7fd249d6e9", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "^3.4.1", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.4" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4|^2.0", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/symfony/http-client/tree/v7.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-13T15:35:37+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "20414d96f391677bf80078aa55baece78b82647d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d", + "reference": "20414d96f391677bf80078aa55baece78b82647d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, { "name": "symfony/http-foundation", "version": "v7.0.7", @@ -7994,16 +8097,16 @@ }, { "name": "symfony/mailgun-mailer", - "version": "v7.0.7", + "version": "v7.1.0", "source": { "type": "git", "url": "https://github.com/symfony/mailgun-mailer.git", - "reference": "e9bb8fdbdd79334a8a88bdd233204315abd992c5" + "reference": "aa5afbe846bbc8bde6afe2602f0427834b872f55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/e9bb8fdbdd79334a8a88bdd233204315abd992c5", - "reference": "e9bb8fdbdd79334a8a88bdd233204315abd992c5", + "url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/aa5afbe846bbc8bde6afe2602f0427834b872f55", + "reference": "aa5afbe846bbc8bde6afe2602f0427834b872f55", "shasum": "" }, "require": { @@ -8043,7 +8146,7 @@ "description": "Symfony Mailgun Mailer Bridge", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailgun-mailer/tree/v7.0.7" + "source": "https://github.com/symfony/mailgun-mailer/tree/v7.1.0" }, "funding": [ { @@ -8059,7 +8162,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:29:19+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/mime", @@ -13061,5 +13164,5 @@ "ext-zip": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/config/hashids.php b/config/hashids.php deleted file mode 100644 index 199de1a32..000000000 --- a/config/hashids.php +++ /dev/null @@ -1,15 +0,0 @@ - env('HASHIDS_SALT'), - 'length' => env('HASHIDS_LENGTH', 8), - 'alphabet' => env('HASHIDS_ALPHABET', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'), -]; diff --git a/database/Factories/ApiKeyFactory.php b/database/Factories/ApiKeyFactory.php index bfcb4b98b..3361433b4 100644 --- a/database/Factories/ApiKeyFactory.php +++ b/database/Factories/ApiKeyFactory.php @@ -26,7 +26,7 @@ class ApiKeyFactory extends Factory return [ 'key_type' => ApiKey::TYPE_APPLICATION, 'identifier' => ApiKey::generateTokenIdentifier(ApiKey::TYPE_APPLICATION), - 'token' => $token ?: $token = encrypt(Str::random(ApiKey::KEY_LENGTH)), + 'token' => $token ?: $token = Str::random(ApiKey::KEY_LENGTH), 'allowed_ips' => null, 'memo' => 'Test Function Key', 'created_at' => Carbon::now(), diff --git a/database/Factories/DatabaseFactory.php b/database/Factories/DatabaseFactory.php index 963c52add..d590a0e13 100644 --- a/database/Factories/DatabaseFactory.php +++ b/database/Factories/DatabaseFactory.php @@ -27,7 +27,7 @@ class DatabaseFactory extends Factory 'database' => Str::random(10), 'username' => Str::random(10), 'remote' => '%', - 'password' => $password ?: encrypt('test123'), + 'password' => $password ?: 'test123', 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]; diff --git a/database/Factories/DatabaseHostFactory.php b/database/Factories/DatabaseHostFactory.php index aaf8bbc69..ad76eac44 100644 --- a/database/Factories/DatabaseHostFactory.php +++ b/database/Factories/DatabaseHostFactory.php @@ -3,7 +3,6 @@ namespace Database\Factories; use App\Models\DatabaseHost; -use Illuminate\Support\Facades\Crypt; use Illuminate\Database\Eloquent\Factories\Factory; class DatabaseHostFactory extends Factory @@ -25,7 +24,7 @@ class DatabaseHostFactory extends Factory 'host' => $this->faker->unique()->ipv4(), 'port' => 3306, 'username' => $this->faker->colorName(), - 'password' => Crypt::encrypt($this->faker->word()), + 'password' => $this->faker->word(), ]; } } diff --git a/database/Factories/NodeFactory.php b/database/Factories/NodeFactory.php index 011db38a3..19a1fbf7b 100644 --- a/database/Factories/NodeFactory.php +++ b/database/Factories/NodeFactory.php @@ -5,7 +5,6 @@ namespace Database\Factories; use Ramsey\Uuid\Uuid; use Illuminate\Support\Str; use App\Models\Node; -use Illuminate\Support\Facades\Crypt; use Illuminate\Database\Eloquent\Factories\Factory; class NodeFactory extends Factory @@ -37,7 +36,7 @@ class NodeFactory extends Factory 'cpu_overallocate' => 0, 'upload_size' => 100, 'daemon_token_id' => Str::random(Node::DAEMON_TOKEN_ID_LENGTH), - 'daemon_token' => Crypt::encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH)), + 'daemon_token' => Str::random(Node::DAEMON_TOKEN_LENGTH), 'daemon_listen' => 8080, 'daemon_sftp' => 2022, 'daemon_base' => '/var/lib/panel/volumes', diff --git a/database/migrations/2024_05_31_204646_fix_old_encrypted_values.php b/database/migrations/2024_05_31_204646_fix_old_encrypted_values.php new file mode 100644 index 000000000..301021349 --- /dev/null +++ b/database/migrations/2024_05_31_204646_fix_old_encrypted_values.php @@ -0,0 +1,81 @@ +get(); + foreach ($keys as $key) { + try { + $reEncrypted = encrypt(decrypt($key->token), false); + DB::table('api_keys') + ->where('id', $key->id) + ->update(['token' => $reEncrypted]); + } catch (Exception $exception) { + logger()->error($exception->getMessage()); + } + } + + $databases = DB::table('databases')->get(); + foreach ($databases as $database) { + try { + $reEncrypted = encrypt(decrypt($database->password), false); + DB::table('databases') + ->where('id', $database->id) + ->update(['password' => $reEncrypted]); + } catch (Exception $exception) { + logger()->error($exception->getMessage()); + } + } + + $databaseHosts = DB::table('database_hosts')->get(); + foreach ($databaseHosts as $host) { + try { + $reEncrypted = encrypt(decrypt($host->password), false); + DB::table('database_hosts') + ->where('id', $host->id) + ->update(['password' => $reEncrypted]); + } catch (Exception $exception) { + logger()->error($exception->getMessage()); + } + } + + $nodes = DB::table('nodes')->get(); + foreach ($nodes as $node) { + try { + $reEncrypted = encrypt(decrypt($node->daemon_token), false); + DB::table('nodes') + ->where('id', $node->id) + ->update(['daemon_token' => $reEncrypted]); + } catch (Exception $exception) { + logger()->error($exception->getMessage()); + } + } + + $users = DB::table('users')->get(); + foreach ($users as $user) { + try { + $reEncrypted = encrypt(decrypt($user->totp_secret), false); + DB::table('users') + ->where('id', $user->id) + ->update(['totp_secret' => $reEncrypted]); + } catch (Exception $exception) { + logger()->error($exception->getMessage()); + } + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // No need to do anything + } +}; diff --git a/lang/en/command/messages.php b/lang/en/command/messages.php index 4e640b3fa..50adf0fa3 100644 --- a/lang/en/command/messages.php +++ b/lang/en/command/messages.php @@ -23,7 +23,7 @@ return [ '2fa_disabled' => '2-Factor authentication has been disabled for :email.', ], 'schedule' => [ - 'output_line' => 'Dispatching job for first task in `:schedule` (:hash).', + 'output_line' => 'Dispatching job for first task in `:schedule` (:id).', ], 'maintenance' => [ 'deleting_service_backup' => 'Deleting service backup file :file.', diff --git a/readme.md b/readme.md index 192c2c3dd..b737a25aa 100644 --- a/readme.md +++ b/readme.md @@ -2,6 +2,10 @@ # Pelican Panel +![Total Downloads](https://img.shields.io/github/downloads/pelican-dev/panel/total?style=flat&label=Total%20Downloads&labelColor=rgba(0%2C%2070%2C%20114%2C%201)&color=rgba(255%2C%20255%2C%20255%2C%201)) +![Latest Release](https://img.shields.io/github/v/release/pelican-dev/panel?style=flat&label=Latest%20Release&labelColor=rgba(0%2C%2070%2C%20114%2C%201)&color=rgba(255%2C%20255%2C%20255%2C%201)) + + Pelican Panel is an open-source, web-based application designed for easy management of game servers. It offers a user-friendly interface for deploying, configuring, and managing servers, with features like real-time resource monitoring, Docker container isolation, and extensive customization options. Ideal for both individual gamers and hosting companies, it simplifies server administration without requiring deep technical knowledge. diff --git a/resources/views/admin/api/index.blade.php b/resources/views/admin/api/index.blade.php index 8d848d403..717257187 100644 --- a/resources/views/admin/api/index.blade.php +++ b/resources/views/admin/api/index.blade.php @@ -33,7 +33,7 @@ @foreach($keys as $key) - {{ $key->identifier }}{{ decrypt($key->token) }} + {{ $key->identifier }}{{ $key->token }} {{ $key->memo }} @if(!is_null($key->last_used_at)) diff --git a/resources/views/admin/nodes/index.blade.php b/resources/views/admin/nodes/index.blade.php index 8f003d953..64d7cd033 100644 --- a/resources/views/admin/nodes/index.blade.php +++ b/resources/views/admin/nodes/index.blade.php @@ -49,7 +49,7 @@ @foreach ($nodes as $node) - + {!! $node->maintenance_mode ? ' ' : '' !!}{{ $node->name }} {{ $node->memory }} MiB {{ $node->disk }} MiB diff --git a/tests/Integration/Api/Application/ApplicationApiIntegrationTestCase.php b/tests/Integration/Api/Application/ApplicationApiIntegrationTestCase.php index 183b48c39..e4fe09570 100644 --- a/tests/Integration/Api/Application/ApplicationApiIntegrationTestCase.php +++ b/tests/Integration/Api/Application/ApplicationApiIntegrationTestCase.php @@ -37,7 +37,7 @@ abstract class ApplicationApiIntegrationTestCase extends IntegrationTestCase $this ->withHeader('Accept', 'application/vnd.panel.v1+json') - ->withHeader('Authorization', 'Bearer ' . $this->key->identifier . decrypt($this->key->token)); + ->withHeader('Authorization', 'Bearer ' . $this->key->identifier . $this->key->token); } public function getApiUser(): User @@ -57,7 +57,7 @@ abstract class ApplicationApiIntegrationTestCase extends IntegrationTestCase { $this->key = $this->createApiKey($user, $permissions); - $this->withHeader('Authorization', 'Bearer ' . $this->key->identifier . decrypt($this->key->token)); + $this->withHeader('Authorization', 'Bearer ' . $this->key->identifier . $this->key->token); return $this->key; } diff --git a/tests/Integration/Api/Client/ApiKeyControllerTest.php b/tests/Integration/Api/Client/ApiKeyControllerTest.php index 5e380ad36..462875c59 100644 --- a/tests/Integration/Api/Client/ApiKeyControllerTest.php +++ b/tests/Integration/Api/Client/ApiKeyControllerTest.php @@ -71,7 +71,7 @@ class ApiKeyControllerTest extends ClientApiIntegrationTestCase $key = ApiKey::query()->where('identifier', $response->json('attributes.identifier'))->firstOrFail(); $this->assertJsonTransformedWith($response->json('attributes'), $key); - $response->assertJsonPath('meta.secret_token', decrypt($key->token)); + $response->assertJsonPath('meta.secret_token', $key->token); $this->assertActivityFor('user:api-key.create', $user, [$key, $user]); } diff --git a/tests/Integration/Api/Client/Server/Database/DatabaseAuthorizationTest.php b/tests/Integration/Api/Client/Server/Database/DatabaseAuthorizationTest.php index 0b3b88cd9..b95778104 100644 --- a/tests/Integration/Api/Client/Server/Database/DatabaseAuthorizationTest.php +++ b/tests/Integration/Api/Client/Server/Database/DatabaseAuthorizationTest.php @@ -5,7 +5,6 @@ namespace App\Tests\Integration\Api\Client\Server\Database; use App\Models\Subuser; use App\Models\Database; use App\Models\DatabaseHost; -use App\Contracts\Extensions\HashidsInterface; use App\Services\Databases\DatabasePasswordService; use App\Services\Databases\DatabaseManagementService; use App\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; @@ -39,23 +38,22 @@ class DatabaseAuthorizationTest extends ClientApiIntegrationTestCase ->expects($method === 'POST' ? 'handle' : 'delete') ->andReturn($method === 'POST' ? 'foo' : null); - $hashids = $this->app->make(HashidsInterface::class); // This is the only valid call for this test, accessing the database for the same // server that the API user is the owner of. - $this->actingAs($user)->json($method, $this->link($server1, '/databases/' . $hashids->encode($database1->id) . $endpoint)) + $this->actingAs($user)->json($method, $this->link($server1, '/databases/' . $database1->id . $endpoint)) ->assertStatus($method === 'DELETE' ? 204 : 200); // This request fails because the database is valid for that server but the user // making the request is not authorized to perform that action. - $this->actingAs($user)->json($method, $this->link($server2, '/databases/' . $hashids->encode($database2->id) . $endpoint))->assertForbidden(); + $this->actingAs($user)->json($method, $this->link($server2, '/databases/' . $database2->id . $endpoint))->assertForbidden(); // Both of these should report a 404 error due to the database being linked to // servers that are not the same as the server in the request, or are assigned // to a server for which the user making the request has no access to. - $this->actingAs($user)->json($method, $this->link($server1, '/databases/' . $hashids->encode($database2->id) . $endpoint))->assertNotFound(); - $this->actingAs($user)->json($method, $this->link($server1, '/databases/' . $hashids->encode($database3->id) . $endpoint))->assertNotFound(); - $this->actingAs($user)->json($method, $this->link($server2, '/databases/' . $hashids->encode($database3->id) . $endpoint))->assertNotFound(); - $this->actingAs($user)->json($method, $this->link($server3, '/databases/' . $hashids->encode($database3->id) . $endpoint))->assertNotFound(); + $this->actingAs($user)->json($method, $this->link($server1, '/databases/' . $database2->id . $endpoint))->assertNotFound(); + $this->actingAs($user)->json($method, $this->link($server1, '/databases/' . $database3->id . $endpoint))->assertNotFound(); + $this->actingAs($user)->json($method, $this->link($server2, '/databases/' . $database3->id . $endpoint))->assertNotFound(); + $this->actingAs($user)->json($method, $this->link($server3, '/databases/' . $database3->id . $endpoint))->assertNotFound(); } public static function methodDataProvider(): array diff --git a/tests/Integration/Api/Client/Server/WebsocketControllerTest.php b/tests/Integration/Api/Client/Server/WebsocketControllerTest.php index 6fe802ba1..44d006c7b 100644 --- a/tests/Integration/Api/Client/Server/WebsocketControllerTest.php +++ b/tests/Integration/Api/Client/Server/WebsocketControllerTest.php @@ -62,7 +62,7 @@ class WebsocketControllerTest extends ClientApiIntegrationTestCase $this->assertStringStartsWith('wss://', $connection, 'Failed asserting that websocket connection address has expected "wss://" prefix.'); $this->assertStringEndsWith("/api/servers/$server->uuid/ws", $connection, 'Failed asserting that websocket connection address uses expected Daemon endpoint.'); - $config = Configuration::forSymmetricSigner(new Sha256(), $key = InMemory::plainText($server->node->getDecryptedKey())); + $config = Configuration::forSymmetricSigner(new Sha256(), $key = InMemory::plainText($server->node->daemon_token)); $config->setValidationConstraints(new SignedWith(new Sha256(), $key)); /** @var \Lcobucci\JWT\Token\Plain $token */ $token = $config->parser()->parse($response->json('data.token')); @@ -107,7 +107,7 @@ class WebsocketControllerTest extends ClientApiIntegrationTestCase $response->assertOk(); $response->assertJsonStructure(['data' => ['token', 'socket']]); - $config = Configuration::forSymmetricSigner(new Sha256(), $key = InMemory::plainText($server->node->getDecryptedKey())); + $config = Configuration::forSymmetricSigner(new Sha256(), $key = InMemory::plainText($server->node->daemon_token)); $config->setValidationConstraints(new SignedWith(new Sha256(), $key)); /** @var \Lcobucci\JWT\Token\Plain $token */ $token = $config->parser()->parse($response->json('data.token')); diff --git a/tests/Integration/Api/Client/TwoFactorControllerTest.php b/tests/Integration/Api/Client/TwoFactorControllerTest.php index 39bd3ddc3..24d8a2684 100644 --- a/tests/Integration/Api/Client/TwoFactorControllerTest.php +++ b/tests/Integration/Api/Client/TwoFactorControllerTest.php @@ -85,8 +85,7 @@ class TwoFactorControllerTest extends ClientApiIntegrationTestCase /** @var \PragmaRX\Google2FA\Google2FA $service */ $service = $this->app->make(Google2FA::class); - $secret = decrypt($user->totp_secret); - $token = $service->getCurrentOtp($secret); + $token = $service->getCurrentOtp($user->totp_secret); $response = $this->actingAs($user)->postJson('/api/client/account/two-factor', [ 'code' => $token, diff --git a/tests/Integration/Api/Daemon/DaemonAuthenticateTest.php b/tests/Integration/Api/Daemon/DaemonAuthenticateTest.php index a0eabde89..b18b3e5e8 100644 --- a/tests/Integration/Api/Daemon/DaemonAuthenticateTest.php +++ b/tests/Integration/Api/Daemon/DaemonAuthenticateTest.php @@ -94,7 +94,7 @@ class DaemonAuthenticateTest extends MiddlewareTestCase public function testSuccessfulMiddlewareProcess(): void { $node = Node::factory()->create(); - $node->daemon_token = encrypt('the_same'); + $node->daemon_token = 'the_same'; $node->save(); $this->request->expects('route->getName')->withNoArgs()->andReturn('random.route'); diff --git a/tests/Integration/Api/Remote/SftpAuthenticationControllerTest.php b/tests/Integration/Api/Remote/SftpAuthenticationControllerTest.php index 13eff086b..2affbef73 100644 --- a/tests/Integration/Api/Remote/SftpAuthenticationControllerTest.php +++ b/tests/Integration/Api/Remote/SftpAuthenticationControllerTest.php @@ -229,6 +229,6 @@ class SftpAuthenticationControllerTest extends IntegrationTestCase { $node = $node ?? $this->server->node; - $this->withHeader('Authorization', 'Bearer ' . $node->daemon_token_id . '.' . decrypt($node->daemon_token)); + $this->withHeader('Authorization', 'Bearer ' . $node->daemon_token_id . '.' . $node->daemon_token); } }