diff --git a/app/Http/Requests/Admin/Egg/EggFormRequest.php b/app/Http/Requests/Admin/Egg/EggFormRequest.php index 4cc44f630..065abaf13 100644 --- a/app/Http/Requests/Admin/Egg/EggFormRequest.php +++ b/app/Http/Requests/Admin/Egg/EggFormRequest.php @@ -22,7 +22,7 @@ class EggFormRequest extends AdminFormRequest 'name' => 'required|string|max:191', 'description' => 'nullable|string', 'docker_images' => 'required|string', - 'file_denylist' => 'string', + 'file_denylist' => 'array', 'startup' => 'required|string', 'config_from' => 'sometimes|bail|nullable|numeric', 'config_stop' => 'required_without:config_from|nullable|string|max:191', diff --git a/app/Models/Egg.php b/app/Models/Egg.php index 70e7b305c..e9eb4b738 100644 --- a/app/Models/Egg.php +++ b/app/Models/Egg.php @@ -13,7 +13,7 @@ namespace Pterodactyl\Models; * @property string $docker_image -- deprecated, use $docker_images * @property string $update_url * @property array $docker_images - * @property string $file_denylist + * @property array|null $file_denylist * @property string|null $config_files * @property string|null $config_startup * @property string|null $config_logs @@ -105,6 +105,7 @@ class Egg extends Model 'copy_script_from' => 'integer', 'features' => 'array', 'docker_images' => 'array', + 'file_denylist' => 'array', ]; /** @@ -117,6 +118,8 @@ class Egg extends Model 'description' => 'string|nullable', 'features' => 'array|nullable', 'author' => 'required|string|email', + 'file_denylist' => 'array|nullable', + 'file_denylist.*' => 'string', 'docker_images' => 'required|array|min:1', 'docker_images.*' => 'required|string', 'startup' => 'required|nullable|string', @@ -133,6 +136,7 @@ class Egg extends Model */ protected $attributes = [ 'features' => null, + 'file_denylist' => null, 'config_stop' => null, 'config_startup' => null, 'config_logs' => null, @@ -260,7 +264,7 @@ class Egg extends Model * Returns the features available to this egg from the parent configuration if there are * no features defined for this egg specifically and there is a parent egg configured. * - * @return string + * @return string[]|null */ public function getInheritFileDenylistAttribute() { diff --git a/app/Services/Eggs/Sharing/EggExporterService.php b/app/Services/Eggs/Sharing/EggExporterService.php index 2cdbd9850..f64656150 100644 --- a/app/Services/Eggs/Sharing/EggExporterService.php +++ b/app/Services/Eggs/Sharing/EggExporterService.php @@ -3,6 +3,8 @@ namespace Pterodactyl\Services\Eggs\Sharing; use Carbon\Carbon; +use Illuminate\Support\Collection; +use Pterodactyl\Models\EggVariable; use Pterodactyl\Contracts\Repository\EggRepositoryInterface; class EggExporterService @@ -41,7 +43,9 @@ class EggExporterService 'description' => $egg->description, 'features' => $egg->features, 'images' => $egg->docker_images, - 'file_denylist' => $egg->inherit_file_denylist, + 'file_denylist' => Collection::make($egg->inherit_file_denylist)->filter(function ($value) { + return !empty($value); + }), 'startup' => $egg->startup, 'config' => [ 'files' => $egg->inherit_config_files, @@ -56,10 +60,10 @@ class EggExporterService 'entrypoint' => $egg->copy_script_entry, ], ], - 'variables' => $egg->variables->transform(function ($item) { - return collect($item->toArray())->except([ - 'id', 'egg_id', 'created_at', 'updated_at', - ])->toArray(); + 'variables' => $egg->variables->transform(function (EggVariable $item) { + return Collection::make($item->toArray()) + ->except(['id', 'egg_id', 'created_at', 'updated_at']) + ->toArray(); }), ]; diff --git a/app/Services/Eggs/Sharing/EggImporterService.php b/app/Services/Eggs/Sharing/EggImporterService.php index b3ea33c90..fd6a7f095 100644 --- a/app/Services/Eggs/Sharing/EggImporterService.php +++ b/app/Services/Eggs/Sharing/EggImporterService.php @@ -4,7 +4,9 @@ namespace Pterodactyl\Services\Eggs\Sharing; use Ramsey\Uuid\Uuid; use Pterodactyl\Models\Egg; +use Illuminate\Support\Arr; use Illuminate\Http\UploadedFile; +use Illuminate\Support\Collection; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Contracts\Repository\EggRepositoryInterface; use Pterodactyl\Contracts\Repository\NestRepositoryInterface; @@ -63,43 +65,47 @@ class EggImporterService throw new InvalidFileUploadException(sprintf('The selected file ["%s"] was not in a valid format to import. (is_file: %s is_valid: %s err_code: %s err: %s)', $file->getFilename(), $file->isFile() ? 'true' : 'false', $file->isValid() ? 'true' : 'false', $file->getError(), $file->getErrorMessage())); } - $parsed = json_decode($file->openFile()->fread($file->getSize())); + /** @var array $parsed */ + $parsed = json_decode($file->openFile()->fread($file->getSize()), true); if (json_last_error() !== 0) { throw new BadJsonFormatException(trans('exceptions.nest.importer.json_error', ['error' => json_last_error_msg()])); } - if (object_get($parsed, 'meta.version') !== 'PTDL_v1') { + if (Arr::get($parsed, 'meta.version') !== 'PTDL_v1') { throw new InvalidFileUploadException(trans('exceptions.nest.importer.invalid_json_provided')); } $nest = $this->nestRepository->getWithEggs($nest); $this->connection->beginTransaction(); + /** @var \Pterodactyl\Models\Egg $egg */ $egg = $this->repository->create([ 'uuid' => Uuid::uuid4()->toString(), 'nest_id' => $nest->id, - 'author' => object_get($parsed, 'author'), - 'name' => object_get($parsed, 'name'), - 'description' => object_get($parsed, 'description'), - 'features' => object_get($parsed, 'features'), + 'author' => Arr::get($parsed, 'author'), + 'name' => Arr::get($parsed, 'name'), + 'description' => Arr::get($parsed, 'description'), + 'features' => Arr::get($parsed, 'features'), // Maintain backwards compatability for eggs that are still using the old single image // string format. New eggs can provide an array of Docker images that can be used. - 'docker_images' => object_get($parsed, 'images') ?? [object_get($parsed, 'image')], - 'file_denylist' => implode(PHP_EOL, object_get($parsed, 'file_denylist') ?? []), - 'update_url' => object_get($parsed, 'meta.update_url'), - 'config_files' => object_get($parsed, 'config.files'), - 'config_startup' => object_get($parsed, 'config.startup'), - 'config_logs' => object_get($parsed, 'config.logs'), - 'config_stop' => object_get($parsed, 'config.stop'), - 'startup' => object_get($parsed, 'startup'), - 'script_install' => object_get($parsed, 'scripts.installation.script'), - 'script_entry' => object_get($parsed, 'scripts.installation.entrypoint'), - 'script_container' => object_get($parsed, 'scripts.installation.container'), + 'docker_images' => Arr::get($parsed, 'images') ?? [Arr::get($parsed, 'image')], + 'file_denylist' => Collection::make(Arr::get($parsed, 'file_denylist'))->filter(function ($value) { + return !empty($value); + }), + 'update_url' => Arr::get($parsed, 'meta.update_url'), + 'config_files' => Arr::get($parsed, 'config.files'), + 'config_startup' => Arr::get($parsed, 'config.startup'), + 'config_logs' => Arr::get($parsed, 'config.logs'), + 'config_stop' => Arr::get($parsed, 'config.stop'), + 'startup' => Arr::get($parsed, 'startup'), + 'script_install' => Arr::get($parsed, 'scripts.installation.script'), + 'script_entry' => Arr::get($parsed, 'scripts.installation.entrypoint'), + 'script_container' => Arr::get($parsed, 'scripts.installation.container'), 'copy_script_from' => null, ], true, true); - collect($parsed->variables)->each(function ($variable) use ($egg) { - $this->eggVariableRepository->create(array_merge((array) $variable, [ + Collection::make($parsed['variables'] ?? [])->each(function (array $variable) use ($egg) { + $this->eggVariableRepository->create(array_merge($variable, [ 'egg_id' => $egg->id, ])); }); diff --git a/app/Services/Servers/ServerConfigurationStructureService.php b/app/Services/Servers/ServerConfigurationStructureService.php index 46e50e012..cfef71c6f 100644 --- a/app/Services/Servers/ServerConfigurationStructureService.php +++ b/app/Services/Servers/ServerConfigurationStructureService.php @@ -80,7 +80,7 @@ class ServerConfigurationStructureService }), 'egg' => [ 'id' => $server->egg->uuid, - 'file_denylist' => explode(PHP_EOL, $server->egg->inherit_file_denylist), + 'file_denylist' => $server->egg->inherit_file_denylist, ], ]; } diff --git a/app/Transformers/Api/Application/EggTransformer.php b/app/Transformers/Api/Application/EggTransformer.php index a4d3d6985..87debe216 100644 --- a/app/Transformers/Api/Application/EggTransformer.php +++ b/app/Transformers/Api/Application/EggTransformer.php @@ -5,6 +5,7 @@ namespace Pterodactyl\Transformers\Api\Application; use Pterodactyl\Models\Egg; use Pterodactyl\Models\Nest; use Pterodactyl\Models\Server; +use Illuminate\Support\Collection; use Pterodactyl\Models\EggVariable; use Pterodactyl\Services\Acl\Api\AdminAcl; @@ -16,7 +17,11 @@ class EggTransformer extends BaseTransformer * @var array */ protected $availableIncludes = [ - 'nest', 'servers', 'config', 'script', 'variables', + 'nest', + 'servers', + 'config', + 'script', + 'variables', ]; /** @@ -52,7 +57,7 @@ class EggTransformer extends BaseTransformer 'startup' => json_decode($model->config_startup, true), 'stop' => $model->config_stop, 'logs' => json_decode($model->config_logs, true), - 'file_denylist' => explode(PHP_EOL, $model->file_denylist), + 'file_denylist' => $model->file_denylist, 'extends' => $model->config_from, ], 'startup' => $model->startup, diff --git a/database/migrations/2021_01_26_210502_update_file_denylist_to_json.php b/database/migrations/2021_01_26_210502_update_file_denylist_to_json.php new file mode 100644 index 000000000..90545e723 --- /dev/null +++ b/database/migrations/2021_01_26_210502_update_file_denylist_to_json.php @@ -0,0 +1,40 @@ +dropColumn('file_denylist'); + }); + + Schema::table('eggs', function (Blueprint $table) { + $table->json('file_denylist')->nullable()->after('docker_images'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('file_denylist'); + }); + + Schema::table('eggs', function (Blueprint $table) { + $table->text('file_denylist')->after('docker_images'); + }); + } +}