From 87a4275ccfc8f80594af79513af6231604af0504 Mon Sep 17 00:00:00 2001 From: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com> Date: Thu, 24 Oct 2024 03:20:15 +0000 Subject: [PATCH] Required adjustments --- .../ApiKeyResource/Pages/CreateApiKey.php | 2 +- .../Resources/NodeResource/Pages/EditNode.php | 156 +++++++++++++----- .../Api/Client/ApiKeyController.php | 2 +- app/Models/ApiKey.php | 15 +- app/Models/Traits/HasAccessTokens.php | 2 +- app/Services/Api/KeyCreationService.php | 2 +- app/Services/Nodes/NodeAutoDeployService.php | 3 +- config/api.php | 9 + database/Factories/ApiKeyFactory.php | 2 +- .../Api/Client/ApiKeyControllerTest.php | 2 +- 10 files changed, 138 insertions(+), 57 deletions(-) create mode 100644 config/api.php diff --git a/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php b/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php index 11e1fae032b..db522acc20d 100644 --- a/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php +++ b/app/Filament/Resources/ApiKeyResource/Pages/CreateApiKey.php @@ -23,7 +23,7 @@ public function form(Form $form): Form return $form ->schema([ Hidden::make('identifier')->default(ApiKey::generateTokenIdentifier(ApiKey::TYPE_APPLICATION)), - Hidden::make('token')->default(str_random(ApiKey::KEY_LENGTH)), + Hidden::make('token')->default(str_random(config('api.key.secret_length', 32))), Hidden::make('user_id') ->default(auth()->user()->id) diff --git a/app/Filament/Resources/NodeResource/Pages/EditNode.php b/app/Filament/Resources/NodeResource/Pages/EditNode.php index 11f77abfe99..b5f3804e1ed 100644 --- a/app/Filament/Resources/NodeResource/Pages/EditNode.php +++ b/app/Filament/Resources/NodeResource/Pages/EditNode.php @@ -8,6 +8,7 @@ use App\Services\Nodes\NodeUpdateService; use Filament\Actions; use Filament\Forms; +use Filament\Forms\Components\Actions as FormActions; use Filament\Forms\Components\Fieldset; use Filament\Forms\Components\Grid; use Filament\Forms\Components\Placeholder; @@ -151,19 +152,9 @@ public function form(Forms\Form $form): Forms\Form true => 'success', false => 'danger', ]) - ->columnSpan([ - 'default' => 1, - 'sm' => 1, - 'md' => 1, - 'lg' => 1, - ]), + ->columnSpan(1), TextInput::make('daemon_listen') - ->columnSpan([ - 'default' => 1, - 'sm' => 1, - 'md' => 1, - 'lg' => 1, - ]) + ->columnSpan(1) ->label(trans('strings.port')) ->helperText('If you are running the daemon behind Cloudflare you should set the daemon port to 8443 to allow websocket proxying over SSL.') ->minValue(1) @@ -184,12 +175,7 @@ public function form(Forms\Form $form): Forms\Form ->maxLength(100), ToggleButtons::make('scheme') ->label('Communicate over SSL') - ->columnSpan([ - 'default' => 1, - 'sm' => 1, - 'md' => 1, - 'lg' => 1, - ]) + ->columnSpan(1) ->inline() ->helperText(function (Get $get) { if (request()->isSecure()) { @@ -217,23 +203,48 @@ public function form(Forms\Form $form): Forms\Form ]) ->default(fn () => request()->isSecure() ? 'https' : 'http'), ]), Tab::make('Advanced Settings') - ->columns(['default' => 1, 'sm' => 1, 'md' => 4, 'lg' => 6]) + ->columns([ + 'default' => 1, + 'sm' => 1, + 'md' => 4, + 'lg' => 6, + ]) ->icon('tabler-server-cog') ->schema([ TextInput::make('id') ->label('Node ID') - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 1]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 2, + 'lg' => 1, + ]) ->disabled(), TextInput::make('uuid') - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 2, + 'lg' => 2, + ]) ->label('Node UUID') ->hintAction(CopyAction::make()) ->disabled(), TagsInput::make('tags') - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 2]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 2, + 'lg' => 2, + ]) ->placeholder('Add Tags'), TextInput::make('upload_size') - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 2, 'lg' => 1]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 2, + 'lg' => 1, + ]) ->label('Upload Limit') ->hintIcon('tabler-question-mark') ->hintIconTooltip('Enter the maximum size of files that can be uploaded through the web-based file manager.') @@ -242,7 +253,12 @@ public function form(Forms\Form $form): Forms\Form ->maxValue(1024) ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB'), TextInput::make('daemon_sftp') - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 3]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 3, + ]) ->label('SFTP Port') ->minValue(1) ->maxValue(65535) @@ -250,11 +266,21 @@ public function form(Forms\Form $form): Forms\Form ->required() ->integer(), TextInput::make('daemon_sftp_alias') - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 3]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 3, + ]) ->label('SFTP Alias') ->helperText('Display alias for the SFTP address. Leave empty to use the Node FQDN.'), ToggleButtons::make('public') - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 3]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 3, + ]) ->label('Use Node for deployment?')->inline() ->options([ true => 'Yes', @@ -265,7 +291,12 @@ public function form(Forms\Form $form): Forms\Form false => 'danger', ]), ToggleButtons::make('maintenance_mode') - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 3]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 3, + ]) ->label('Maintenance Mode')->inline() ->hinticon('tabler-question-mark') ->hintIconTooltip("If the node is marked 'Under Maintenance' users won't be able to access servers that are on this node.") @@ -278,7 +309,12 @@ public function form(Forms\Form $form): Forms\Form true => 'danger', ]), Grid::make() - ->columns(['default' => 1, 'sm' => 1, 'md' => 3, 'lg' => 6]) + ->columns([ + 'default' => 1, + 'sm' => 1, + 'md' => 3, + 'lg' => 6, + ]) ->columnSpanFull() ->schema([ ToggleButtons::make('unlimited_mem') @@ -295,14 +331,24 @@ public function form(Forms\Form $form): Forms\Form true => 'primary', false => 'warning', ]) - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 2]), + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 2, + ]), TextInput::make('memory') ->dehydratedWhenHidden() ->hidden(fn (Get $get) => $get('unlimited_mem')) ->label('Memory Limit')->inlineLabel() ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB') ->required() - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 2]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 2, + ]) ->numeric() ->minValue(0), TextInput::make('memory_overallocate') @@ -312,14 +358,24 @@ public function form(Forms\Form $form): Forms\Form ->hidden(fn (Get $get) => $get('unlimited_mem')) ->hintIcon('tabler-question-mark') ->hintIconTooltip('The % allowable to go over the set limit.') - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 2]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 2, + ]) ->numeric() ->minValue(-1) ->maxValue(100) ->suffix('%'), ]), Grid::make() - ->columns(['default' => 1, 'sm' => 1, 'md' => 3, 'lg' => 6]) + ->columns([ + 'default' => 1, + 'sm' => 1, + 'md' => 3, + 'lg' => 6, + ]) ->schema([ ToggleButtons::make('unlimited_disk') ->label('Disk')->inlineLabel()->inline() @@ -335,14 +391,24 @@ public function form(Forms\Form $form): Forms\Form true => 'primary', false => 'warning', ]) - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 2]), + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 2, + ]), TextInput::make('disk') ->dehydratedWhenHidden() ->hidden(fn (Get $get) => $get('unlimited_disk')) ->label('Disk Limit')->inlineLabel() ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB') ->required() - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 2]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 2, + ]) ->numeric() ->minValue(0), TextInput::make('disk_overallocate') @@ -351,7 +417,12 @@ public function form(Forms\Form $form): Forms\Form ->label('Overallocate')->inlineLabel() ->hintIcon('tabler-question-mark') ->hintIconTooltip('The % allowable to go over the set limit.') - ->columnSpan(['default' => 1, 'sm' => 1, 'md' => 1, 'lg' => 2]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 2, + ]) ->required() ->numeric() ->minValue(-1) @@ -417,8 +488,8 @@ public function form(Forms\Form $form): Forms\Form Grid::make() ->columns() ->schema([ - Forms\Components\Actions::make([ - Forms\Components\Actions\Action::make('autoDeploy') + FormActions::make([ + FormActions\Action::make('autoDeploy') ->label('Auto Deploy Command') ->color('primary') ->modalHeading('Auto Deploy Command') @@ -442,12 +513,7 @@ public function form(Forms\Form $form): Forms\Form false => 'primary', true => 'success', ]) - ->columnSpan([ - 'default' => 1, - 'sm' => 1, - 'md' => 1, - 'lg' => 1, - ]), + ->columnSpan(1), Textarea::make('generatedToken') ->label('To auto-configure your node run the following command:') ->readOnly() @@ -460,8 +526,8 @@ public function form(Forms\Form $form): Forms\Form $form->fill(); }), ])->fullWidth(), - Forms\Components\Actions::make([ - Forms\Components\Actions\Action::make('resetKey') + FormActions::make([ + FormActions\Action::make('resetKey') ->label('Reset Daemon Token') ->color('danger') ->requiresConfirmation() diff --git a/app/Http/Controllers/Api/Client/ApiKeyController.php b/app/Http/Controllers/Api/Client/ApiKeyController.php index 13eb728aac4..aa2f0e4f3a2 100644 --- a/app/Http/Controllers/Api/Client/ApiKeyController.php +++ b/app/Http/Controllers/Api/Client/ApiKeyController.php @@ -29,7 +29,7 @@ public function index(ClientApiRequest $request): array */ public function store(StoreApiKeyRequest $request): array { - if ($request->user()->apiKeys->count() >= ApiKey::API_KEYS_LIMIT) { + if ($request->user()->apiKeys->count() >= config('api.key.limit', 25)) { throw new DisplayException('You have reached the account limit for number of API keys.'); } diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php index edf8ebb4392..3506a96a007 100644 --- a/app/Models/ApiKey.php +++ b/app/Models/ApiKey.php @@ -66,15 +66,18 @@ class ApiKey extends Model /** * Maximum number of Api keys that a user can have. + * + * @deprecated */ - public const API_KEYS_LIMIT = 24; + public const API_KEYS_LIMIT = 25; + /** * Different API keys that can exist on the system. */ public const TYPE_NONE = 0; public const TYPE_ACCOUNT = 1; - + public const TYPE_APPLICATION = 2; /* @deprecated */ @@ -85,12 +88,16 @@ class ApiKey extends Model /** * The length of API key identifiers. + * + * @deprecated */ public const IDENTIFIER_LENGTH = 16; /** * The length of the actual API key that is encrypted and stored * in the database. + * + * @deprecated */ public const KEY_LENGTH = 32; @@ -203,7 +210,7 @@ public function tokenable(): BelongsTo */ public static function findToken(string $token): ?self { - $identifier = substr($token, 0, self::IDENTIFIER_LENGTH); + $identifier = substr($token, 0, config('api.key.identifier_length', 16)); $model = static::where('identifier', $identifier)->first(); if (!is_null($model) && $model->token === substr($token, strlen($identifier))) { @@ -230,6 +237,6 @@ public static function generateTokenIdentifier(int $type): string { $prefix = self::getPrefixForType($type); - return $prefix . Str::random(self::IDENTIFIER_LENGTH - strlen($prefix)); + return $prefix . Str::random(config('api.key.identifier_length', 16) - strlen($prefix)); } } diff --git a/app/Models/Traits/HasAccessTokens.php b/app/Models/Traits/HasAccessTokens.php index 14a0f685bdb..4159928241e 100644 --- a/app/Models/Traits/HasAccessTokens.php +++ b/app/Models/Traits/HasAccessTokens.php @@ -31,7 +31,7 @@ public function createToken(?string $memo, ?array $ips): NewAccessToken 'user_id' => $this->id, 'key_type' => ApiKey::TYPE_ACCOUNT, 'identifier' => ApiKey::generateTokenIdentifier(ApiKey::TYPE_ACCOUNT), - 'token' => $plain = Str::random(ApiKey::KEY_LENGTH), + 'token' => $plain = Str::random(config('api.key.secret_length', 32)), 'memo' => $memo ?? '', 'allowed_ips' => $ips ?? [], ]); diff --git a/app/Services/Api/KeyCreationService.php b/app/Services/Api/KeyCreationService.php index 7fde94ba22c..ae1241a64d5 100644 --- a/app/Services/Api/KeyCreationService.php +++ b/app/Services/Api/KeyCreationService.php @@ -31,7 +31,7 @@ public function handle(array $data, array $permissions = []): ApiKey $data = array_merge($data, [ 'key_type' => $this->keyType, 'identifier' => ApiKey::generateTokenIdentifier($this->keyType), - 'token' => str_random(ApiKey::KEY_LENGTH), + 'token' => str_random(config('api.key.secret_length', 32)), ]); if ($this->keyType === ApiKey::TYPE_APPLICATION) { diff --git a/app/Services/Nodes/NodeAutoDeployService.php b/app/Services/Nodes/NodeAutoDeployService.php index 8d85aa6c1ca..b293535b956 100644 --- a/app/Services/Nodes/NodeAutoDeployService.php +++ b/app/Services/Nodes/NodeAutoDeployService.php @@ -27,7 +27,6 @@ public function handle(Request $request, Node $node, ?bool $docker = false): ?st { /** @var ApiKey|null $key */ $key = ApiKey::query() - ->where('user_id', $request->user()->id) ->where('key_type', ApiKey::TYPE_APPLICATION) ->where('r_nodes', 1) ->first(); @@ -47,7 +46,7 @@ public function handle(Request $request, Node $node, ?bool $docker = false): ?st sprintf( '%s wings configure --panel-url %s --token %s --node %d%s', $docker ? 'docker compose exec -it' : 'sudo', - config('app.url'), + route('index'), $token, $node->id, $request->isSecure() ? '' : ' --allow-insecure' diff --git a/config/api.php b/config/api.php new file mode 100644 index 00000000000..c70693106b7 --- /dev/null +++ b/config/api.php @@ -0,0 +1,9 @@ + [ + 'limit' => env('API_KEYS_LIMIT', 25), + 'identifier_length' => env('API_KEYS_IDENTIFIER_LENGTH', 16), + 'secret_length' => env('API_KEYS_SECRET_LENGTH', 32), + ], +]; diff --git a/database/Factories/ApiKeyFactory.php b/database/Factories/ApiKeyFactory.php index 1b258767b74..211131bb132 100644 --- a/database/Factories/ApiKeyFactory.php +++ b/database/Factories/ApiKeyFactory.php @@ -26,7 +26,7 @@ public function definition(): array return [ 'key_type' => ApiKey::TYPE_APPLICATION, 'identifier' => ApiKey::generateTokenIdentifier(ApiKey::TYPE_APPLICATION), - 'token' => $token ?: $token = Str::random(ApiKey::KEY_LENGTH), + 'token' => $token ?: $token = Str::random(config('api.key.secret_length', 32)), 'allowed_ips' => [], 'memo' => 'Test Function Key', 'created_at' => Carbon::now(), diff --git a/tests/Integration/Api/Client/ApiKeyControllerTest.php b/tests/Integration/Api/Client/ApiKeyControllerTest.php index 2fb96c08978..39d6d78a309 100644 --- a/tests/Integration/Api/Client/ApiKeyControllerTest.php +++ b/tests/Integration/Api/Client/ApiKeyControllerTest.php @@ -104,7 +104,7 @@ public function testApiKeyLimitIsApplied(): void { /** @var \App\Models\User $user */ $user = User::factory()->create(); - ApiKey::factory()->times(ApiKey::API_KEYS_LIMIT + 1)->for($user)->create([ + ApiKey::factory()->times(config('api.key.limit', 25))->for($user)->create([ 'key_type' => ApiKey::TYPE_ACCOUNT, ]);