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
+&color=rgba(255%2C%20255%2C%20255%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);
}
}