Skip to content

Commit 69f27ed

Browse files
committed
Update and test variable validator logic
1 parent d8228f2 commit 69f27ed

File tree

3 files changed

+152
-226
lines changed

3 files changed

+152
-226
lines changed

app/Services/Servers/VariableValidatorService.php

Lines changed: 15 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,15 @@
1111

1212
use Pterodactyl\Models\User;
1313
use Illuminate\Support\Collection;
14+
use Pterodactyl\Models\EggVariable;
1415
use Illuminate\Validation\ValidationException;
1516
use Pterodactyl\Traits\Services\HasUserLevels;
16-
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
1717
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
18-
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
19-
use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface;
2018

2119
class VariableValidatorService
2220
{
2321
use HasUserLevels;
2422

25-
/**
26-
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface
27-
*/
28-
private $optionVariableRepository;
29-
30-
/**
31-
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
32-
*/
33-
private $serverRepository;
34-
35-
/**
36-
* @var \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface
37-
*/
38-
private $serverVariableRepository;
39-
4023
/**
4124
* @var \Illuminate\Contracts\Validation\Factory
4225
*/
@@ -45,20 +28,10 @@ class VariableValidatorService
4528
/**
4629
* VariableValidatorService constructor.
4730
*
48-
* @param \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface $optionVariableRepository
49-
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository
50-
* @param \Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface $serverVariableRepository
5131
* @param \Illuminate\Contracts\Validation\Factory $validator
5232
*/
53-
public function __construct(
54-
EggVariableRepositoryInterface $optionVariableRepository,
55-
ServerRepositoryInterface $serverRepository,
56-
ServerVariableRepositoryInterface $serverVariableRepository,
57-
ValidationFactory $validator
58-
) {
59-
$this->optionVariableRepository = $optionVariableRepository;
60-
$this->serverRepository = $serverRepository;
61-
$this->serverVariableRepository = $serverVariableRepository;
33+
public function __construct(ValidationFactory $validator)
34+
{
6235
$this->validator = $validator;
6336
}
6437

@@ -72,16 +45,18 @@ public function __construct(
7245
*/
7346
public function handle(int $egg, array $fields = []): Collection
7447
{
75-
$variables = $this->optionVariableRepository->findWhere([['egg_id', '=', $egg]]);
76-
77-
$data = $rules = $customAttributes = [];
78-
foreach ($variables as $variable) {
48+
$query = EggVariable::query()->where('egg_id', $egg);
49+
if (! $this->isUserLevel(User::USER_LEVEL_ADMIN)) {
7950
// Don't attempt to validate variables if they aren't user editable
8051
// and we're not running this at an admin level.
81-
if (! $variable->user_editable && ! $this->isUserLevel(User::USER_LEVEL_ADMIN)) {
82-
continue;
83-
}
52+
$query = $query->where('user_editable', true)->where('user_viewable', true);
53+
}
54+
55+
/** @var \Pterodactyl\Models\EggVariable[] $variables */
56+
$variables = $query->get();
8457

58+
$data = $rules = $customAttributes = [];
59+
foreach ($variables as $variable) {
8560
$data['environment'][$variable->env_variable] = array_get($fields, $variable->env_variable);
8661
$rules['environment.' . $variable->env_variable] = $variable->rules;
8762
$customAttributes['environment.' . $variable->env_variable] = trans('validation.internal.variable_value', ['env' => $variable->name]);
@@ -92,23 +67,12 @@ public function handle(int $egg, array $fields = []): Collection
9267
throw new ValidationException($validator);
9368
}
9469

95-
$response = $variables->filter(function ($item) {
96-
// Skip doing anything if user is not an admin and variable is not user viewable or editable.
97-
if (! $this->isUserLevel(User::USER_LEVEL_ADMIN) && (! $item->user_editable || ! $item->user_viewable)) {
98-
return false;
99-
}
100-
101-
return true;
102-
})->map(function ($item) use ($fields) {
103-
return (object) [
70+
return Collection::make($variables)->map(function ($item) use ($fields) {
71+
return (object)[
10472
'id' => $item->id,
10573
'key' => $item->env_variable,
106-
'value' => array_get($fields, $item->env_variable),
74+
'value' => $fields[$item->env_variable] ?? null,
10775
];
108-
})->filter(function ($item) {
109-
return is_object($item);
11076
});
111-
112-
return $response;
11377
}
11478
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<?php
2+
3+
namespace Pterodactyl\Tests\Integration\Services\Servers;
4+
5+
use Pterodactyl\Models\Egg;
6+
use Pterodactyl\Models\User;
7+
use Illuminate\Support\Collection;
8+
use Illuminate\Validation\ValidationException;
9+
use Pterodactyl\Tests\Integration\IntegrationTestCase;
10+
use Pterodactyl\Services\Servers\VariableValidatorService;
11+
12+
class VariableValidatorServiceTest extends IntegrationTestCase
13+
{
14+
/**
15+
* Test that enviornment variables for a server are validated as expected.
16+
*/
17+
public function testEnvironmentVariablesCanBeValidated()
18+
{
19+
/** @noinspection PhpParamsInspection */
20+
$egg = $this->cloneEggAndVariables(Egg::query()->findOrFail(1));
21+
22+
try {
23+
$this->getService()->handle($egg->id, [
24+
'BUNGEE_VERSION' => '1.2.3',
25+
]);
26+
27+
$this->assertTrue(false, 'This statement should not be reached.');
28+
} catch (ValidationException $exception) {
29+
$errors = $exception->errors();
30+
31+
$this->assertCount(2, $errors);
32+
$this->assertArrayHasKey('environment.BUNGEE_VERSION', $errors);
33+
$this->assertArrayHasKey('environment.SERVER_JARFILE', $errors);
34+
$this->assertSame('The Bungeecord Version variable may only contain letters and numbers.', $errors['environment.BUNGEE_VERSION'][0]);
35+
$this->assertSame('The Bungeecord Jar File variable field is required.', $errors['environment.SERVER_JARFILE'][0]);
36+
}
37+
38+
$response = $this->getService()->handle($egg->id, [
39+
'BUNGEE_VERSION' => '1234',
40+
'SERVER_JARFILE' => 'server.jar',
41+
]);
42+
43+
$this->assertInstanceOf(Collection::class, $response);
44+
$this->assertCount(2, $response);
45+
$this->assertSame('BUNGEE_VERSION', $response->get(0)->key);
46+
$this->assertSame('1234', $response->get(0)->value);
47+
$this->assertSame('SERVER_JARFILE', $response->get(1)->key);
48+
$this->assertSame('server.jar', $response->get(1)->value);
49+
}
50+
51+
/**
52+
* Test that variables that are user_editable=false do not get validated (or returned) by
53+
* the handler.
54+
*/
55+
public function testNormalUserCannotValidateNonUserEditableVariables()
56+
{
57+
/** @noinspection PhpParamsInspection */
58+
$egg = $this->cloneEggAndVariables(Egg::query()->findOrFail(1));
59+
$egg->variables()->first()->update([
60+
'user_editable' => false,
61+
]);
62+
63+
$response = $this->getService()->handle($egg->id, [
64+
// This is an invalid value, but it shouldn't cause any issues since it should be skipped.
65+
'BUNGEE_VERSION' => '1.2.3',
66+
'SERVER_JARFILE' => 'server.jar',
67+
]);
68+
69+
$this->assertInstanceOf(Collection::class, $response);
70+
$this->assertCount(1, $response);
71+
$this->assertSame('SERVER_JARFILE', $response->get(0)->key);
72+
$this->assertSame('server.jar', $response->get(0)->value);
73+
}
74+
75+
public function testEnvironmentVariablesCanBeUpdatedAsAdmin()
76+
{
77+
/** @noinspection PhpParamsInspection */
78+
$egg = $this->cloneEggAndVariables(Egg::query()->findOrFail(1));
79+
$egg->variables()->first()->update([
80+
'user_editable' => false,
81+
]);
82+
83+
try {
84+
$this->getService()->setUserLevel(User::USER_LEVEL_ADMIN)->handle($egg->id, [
85+
'BUNGEE_VERSION' => '1.2.3',
86+
'SERVER_JARFILE' => 'server.jar',
87+
]);
88+
89+
$this->assertTrue(false, 'This statement should not be reached.');
90+
} catch (ValidationException $exception) {
91+
$this->assertCount(1, $exception->errors());
92+
$this->assertArrayHasKey('environment.BUNGEE_VERSION', $exception->errors());
93+
}
94+
95+
96+
$response = $this->getService()->setUserLevel(User::USER_LEVEL_ADMIN)->handle($egg->id, [
97+
'BUNGEE_VERSION' => '123',
98+
'SERVER_JARFILE' => 'server.jar',
99+
]);
100+
101+
$this->assertInstanceOf(Collection::class, $response);
102+
$this->assertCount(2, $response);
103+
$this->assertSame('BUNGEE_VERSION', $response->get(0)->key);
104+
$this->assertSame('123', $response->get(0)->value);
105+
$this->assertSame('SERVER_JARFILE', $response->get(1)->key);
106+
$this->assertSame('server.jar', $response->get(1)->value);
107+
}
108+
109+
public function testNullableEnvironmentVariablesCanBeUsedCorrectly()
110+
{
111+
/** @noinspection PhpParamsInspection */
112+
$egg = $this->cloneEggAndVariables(Egg::query()->findOrFail(1));
113+
$egg->variables()->where('env_variable', '!=', 'BUNGEE_VERSION')->delete();
114+
115+
$egg->variables()->update(['rules' => 'nullable|string']);
116+
117+
$response = $this->getService()->handle($egg->id, []);
118+
$this->assertCount(1, $response);
119+
$this->assertNull($response->get(0)->value);
120+
121+
$response = $this->getService()->handle($egg->id, ['BUNGEE_VERSION' => null]);
122+
$this->assertCount(1, $response);
123+
$this->assertNull($response->get(0)->value);
124+
125+
$response = $this->getService()->handle($egg->id, ['BUNGEE_VERSION' => '']);
126+
$this->assertCount(1, $response);
127+
$this->assertSame('', $response->get(0)->value);
128+
}
129+
130+
/**
131+
* @return \Pterodactyl\Services\Servers\VariableValidatorService
132+
*/
133+
private function getService()
134+
{
135+
return $this->app->make(VariableValidatorService::class);
136+
}
137+
}

0 commit comments

Comments
 (0)