Fix issues with validation in admin CP for server variables, closes #780

This commit is contained in:
Dane Everitt 2017-11-25 13:15:46 -06:00
parent 0bb44a4972
commit 20c1c74116
6 changed files with 42 additions and 88 deletions

View File

@ -7,6 +7,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines.
### Fixed ### Fixed
* `[beta.2]` — Fixes a bug that would cause an endless exception message stream in the console when attemping to setup environment settings in certain instances. * `[beta.2]` — Fixes a bug that would cause an endless exception message stream in the console when attemping to setup environment settings in certain instances.
* `[beta.2]` — Fixes a bug causing the dropdown menu for a server's egg to display the wrong selected value. * `[beta.2]` — Fixes a bug causing the dropdown menu for a server's egg to display the wrong selected value.
* `[beta.2]` — Fixes a bug that would throw a red page of death when submitting an invalid egg variable value for a server in the Admin CP.
## v0.7.0-beta.2 (Derelict Dermodactylus) ## v0.7.0-beta.2 (Derelict Dermodactylus)
### Fixed ### Fixed

View File

@ -9,6 +9,6 @@
namespace Pterodactyl\Exceptions; namespace Pterodactyl\Exceptions;
class DisplayValidationException extends PterodactylException class DisplayValidationException extends DisplayException
{ {
} }

View File

@ -78,9 +78,10 @@ class StartupModificationService
* @param \Pterodactyl\Models\Server $server * @param \Pterodactyl\Models\Server $server
* @param array $data * @param array $data
* *
* @throws \Pterodactyl\Exceptions\DisplayException * @throws \Illuminate\Validation\ValidationException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
*/ */
public function handle(Server $server, array $data) public function handle(Server $server, array $data)
{ {

View File

@ -11,8 +11,8 @@ namespace Pterodactyl\Services\Servers;
use Pterodactyl\Models\User; use Pterodactyl\Models\User;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Validation\ValidationException;
use Pterodactyl\Traits\Services\HasUserLevels; use Pterodactyl\Traits\Services\HasUserLevels;
use Pterodactyl\Exceptions\DisplayValidationException;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Illuminate\Contracts\Validation\Factory as ValidationFactory; use Illuminate\Contracts\Validation\Factory as ValidationFactory;
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
@ -25,22 +25,22 @@ class VariableValidatorService
/** /**
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface * @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface
*/ */
protected $optionVariableRepository; private $optionVariableRepository;
/** /**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
*/ */
protected $serverRepository; private $serverRepository;
/** /**
* @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface * @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface
*/ */
protected $serverVariableRepository; private $serverVariableRepository;
/** /**
* @var \Illuminate\Contracts\Validation\Factory * @var \Illuminate\Contracts\Validation\Factory
*/ */
protected $validator; private $validator;
/** /**
* VariableValidatorService constructor. * VariableValidatorService constructor.
@ -68,32 +68,32 @@ class VariableValidatorService
* @param int $egg * @param int $egg
* @param array $fields * @param array $fields
* @return \Illuminate\Support\Collection * @return \Illuminate\Support\Collection
* @throws \Illuminate\Validation\ValidationException
*/ */
public function handle(int $egg, array $fields = []): Collection public function handle(int $egg, array $fields = []): Collection
{ {
$variables = $this->optionVariableRepository->findWhere([['egg_id', '=', $egg]]); $variables = $this->optionVariableRepository->findWhere([['egg_id', '=', $egg]]);
$messages = $this->validator->make([], []);
return $variables->map(function ($item) use ($fields) { $response = $variables->map(function ($item) use ($fields, $messages) {
// Skip doing anything if user is not an admin and // Skip doing anything if user is not an admin and
// variable is not user viewable or editable. // variable is not user viewable or editable.
if (! $this->isUserLevel(User::USER_LEVEL_ADMIN) && (! $item->user_editable || ! $item->user_viewable)) { if (! $this->isUserLevel(User::USER_LEVEL_ADMIN) && (! $item->user_editable || ! $item->user_viewable)) {
return false; return false;
} }
$validator = $this->validator->make([ $v = $this->validator->make([
'variable_value' => array_get($fields, $item->env_variable), 'variable_value' => array_get($fields, $item->env_variable),
], [ ], [
'variable_value' => $item->rules, 'variable_value' => $item->rules,
], [], [
'variable_value' => trans('validation.internal.variable_value', ['env' => $item->name]),
]); ]);
if ($validator->fails()) { if ($v->fails()) {
throw new DisplayValidationException(json_encode( foreach ($v->getMessageBag()->all() as $message) {
collect([ $messages->getMessageBag()->add($item->env_variable, $message);
'notice' => [ }
trans('admin/server.exceptions.bad_variable', ['name' => $item->name]),
],
])->merge($validator->errors()->toArray())
));
} }
return (object) [ return (object) [
@ -104,5 +104,11 @@ class VariableValidatorService
})->filter(function ($item) { })->filter(function ($item) {
return is_object($item); return is_object($item);
}); });
if (! empty($messages->getMessageBag()->all())) {
throw new ValidationException($messages);
}
return $response;
} }
} }

View File

@ -85,23 +85,6 @@ return [
'uploaded' => 'The :attribute failed to upload.', 'uploaded' => 'The :attribute failed to upload.',
'url' => 'The :attribute format is invalid.', 'url' => 'The :attribute format is invalid.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Custom Validation Attributes | Custom Validation Attributes
@ -114,4 +97,9 @@ return [
*/ */
'attributes' => [], 'attributes' => [],
// Internal validation logic for Pterodactyl
'internal' => [
'variable_value' => ':env variable',
],
]; ];

View File

@ -1,11 +1,4 @@
<?php <?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Tests\Unit\Services\Servers; namespace Tests\Unit\Services\Servers;
@ -15,8 +8,7 @@ use Pterodactyl\Models\User;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Pterodactyl\Models\EggVariable; use Pterodactyl\Models\EggVariable;
use Illuminate\Contracts\Validation\Factory; use Illuminate\Contracts\Validation\Factory;
use Pterodactyl\Exceptions\PterodactylException; use Illuminate\Validation\ValidationException;
use Pterodactyl\Exceptions\DisplayValidationException;
use Pterodactyl\Services\Servers\VariableValidatorService; use Pterodactyl\Services\Servers\VariableValidatorService;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
@ -27,22 +19,17 @@ class VariableValidatorServiceTest extends TestCase
/** /**
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface|\Mockery\Mock * @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface|\Mockery\Mock
*/ */
protected $optionVariableRepository; private $optionVariableRepository;
/** /**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface|\Mockery\Mock * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface|\Mockery\Mock
*/ */
protected $serverRepository; private $serverRepository;
/** /**
* @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface|\Mockery\Mock * @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface|\Mockery\Mock
*/ */
protected $serverVariableRepository; private $serverVariableRepository;
/**
* @var \Illuminate\Contracts\Validation\Factory|\Mockery\Mock
*/
protected $validator;
/** /**
* Setup tests. * Setup tests.
@ -54,7 +41,6 @@ class VariableValidatorServiceTest extends TestCase
$this->optionVariableRepository = m::mock(EggVariableRepositoryInterface::class); $this->optionVariableRepository = m::mock(EggVariableRepositoryInterface::class);
$this->serverRepository = m::mock(ServerRepositoryInterface::class); $this->serverRepository = m::mock(ServerRepositoryInterface::class);
$this->serverVariableRepository = m::mock(ServerVariableRepositoryInterface::class); $this->serverVariableRepository = m::mock(ServerVariableRepositoryInterface::class);
$this->validator = m::mock(Factory::class);
} }
/** /**
@ -77,13 +63,6 @@ class VariableValidatorServiceTest extends TestCase
$variables = $this->getVariableCollection(); $variables = $this->getVariableCollection();
$this->optionVariableRepository->shouldReceive('findWhere')->with([['egg_id', '=', 1]])->andReturn($variables); $this->optionVariableRepository->shouldReceive('findWhere')->with([['egg_id', '=', 1]])->andReturn($variables);
$this->validator->shouldReceive('make')->with([
'variable_value' => 'Test_SomeValue_0',
], [
'variable_value' => $variables[0]->rules,
])->once()->andReturnSelf();
$this->validator->shouldReceive('fails')->withNoArgs()->once()->andReturn(false);
$response = $this->getService()->handle(1, [ $response = $this->getService()->handle(1, [
$variables[0]->env_variable => 'Test_SomeValue_0', $variables[0]->env_variable => 'Test_SomeValue_0',
$variables[1]->env_variable => 'Test_SomeValue_1', $variables[1]->env_variable => 'Test_SomeValue_1',
@ -112,15 +91,6 @@ class VariableValidatorServiceTest extends TestCase
$variables = $this->getVariableCollection(); $variables = $this->getVariableCollection();
$this->optionVariableRepository->shouldReceive('findWhere')->with([['egg_id', '=', 1]])->andReturn($variables); $this->optionVariableRepository->shouldReceive('findWhere')->with([['egg_id', '=', 1]])->andReturn($variables);
foreach ($variables as $key => $variable) {
$this->validator->shouldReceive('make')->with([
'variable_value' => 'Test_SomeValue_' . $key,
], [
'variable_value' => $variables[$key]->rules,
])->once()->andReturnSelf();
$this->validator->shouldReceive('fails')->withNoArgs()->once()->andReturn(false);
}
$service = $this->getService(); $service = $this->getService();
$service->setUserLevel(User::USER_LEVEL_ADMIN); $service->setUserLevel(User::USER_LEVEL_ADMIN);
$response = $service->handle(1, [ $response = $service->handle(1, [
@ -152,28 +122,16 @@ class VariableValidatorServiceTest extends TestCase
$variables = $this->getVariableCollection(); $variables = $this->getVariableCollection();
$this->optionVariableRepository->shouldReceive('findWhere')->with([['egg_id', '=', 1]])->andReturn($variables); $this->optionVariableRepository->shouldReceive('findWhere')->with([['egg_id', '=', 1]])->andReturn($variables);
$this->validator->shouldReceive('make')->with([
'variable_value' => null,
], [
'variable_value' => $variables[0]->rules,
])->once()->andReturnSelf();
$this->validator->shouldReceive('fails')->withNoArgs()->once()->andReturn(true);
$this->validator->shouldReceive('errors')->withNoArgs()->once()->andReturnSelf();
$this->validator->shouldReceive('toArray')->withNoArgs()->once()->andReturn([]);
try { try {
$this->getService()->handle(1, [$variables[0]->env_variable => null]); $this->getService()->handle(1, [$variables[0]->env_variable => null]);
} catch (PterodactylException $exception) { } catch (ValidationException $exception) {
$this->assertInstanceOf(DisplayValidationException::class, $exception); $messages = $exception->validator->getMessageBag()->all();
$decoded = json_decode($exception->getMessage()); $this->assertNotEmpty($messages);
$this->assertEquals(0, json_last_error(), 'Assert that response is decodable JSON.'); $this->assertSame(1, count($messages));
$this->assertObjectHasAttribute('notice', $decoded); $this->assertSame(trans('validation.required', [
$this->assertEquals( 'attribute' => trans('validation.internal.variable_value', ['env' => $variables[0]->name]),
trans('admin/server.exceptions.bad_variable', ['name' => $variables[0]->name]), ]), $messages[0]);
$decoded->notice[0]
);
} }
} }
@ -205,7 +163,7 @@ class VariableValidatorServiceTest extends TestCase
$this->optionVariableRepository, $this->optionVariableRepository,
$this->serverRepository, $this->serverRepository,
$this->serverVariableRepository, $this->serverVariableRepository,
$this->validator $this->app->make(Factory::class)
); );
} }
} }