Skip to content

Commit d8228f2

Browse files
committed
Allow passing empty values through for variables, covers with test, closes pterodactyl#2433
1 parent bf6e1ce commit d8228f2

File tree

4 files changed

+232
-2
lines changed

4 files changed

+232
-2
lines changed

app/Http/Controllers/Api/Client/Servers/StartupController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public function update(UpdateStartupVariableRequest $request, Server $server)
100100
'server_id' => $server->id,
101101
'variable_id' => $variable->id,
102102
], [
103-
'variable_value' => $request->input('value'),
103+
'variable_value' => $request->input('value') ?? '',
104104
]);
105105

106106
$variable = $variable->refresh();

app/Http/Requests/Api/Client/Servers/Startup/UpdateStartupVariableRequest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public function rules(): array
2424
{
2525
return [
2626
'key' => 'required|string',
27-
'value' => 'present|string',
27+
'value' => 'present',
2828
];
2929
}
3030
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace Pterodactyl\Tests\Integration\Api\Client\Server\Startup;
4+
5+
use Pterodactyl\Models\User;
6+
use Pterodactyl\Models\Permission;
7+
use Pterodactyl\Models\EggVariable;
8+
use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
9+
10+
class GetStartupAndVariablesTest extends ClientApiIntegrationTestCase
11+
{
12+
/**
13+
* Test that the startup command and variables are returned for a server, but only the variables
14+
* that can be viewed by a user (e.g. user_viewable=true).
15+
*
16+
* @param array $permissions
17+
* @dataProvider permissionsDataProvider
18+
*/
19+
public function testStartupVariablesAreReturnedForServer($permissions)
20+
{
21+
/** @var \Pterodactyl\Models\Server $server */
22+
[$user, $server] = $this->generateTestAccount($permissions);
23+
24+
$egg = $this->cloneEggAndVariables($server->egg);
25+
// BUNGEE_VERSION should never be returned back to the user in this API call, either in
26+
// the array of variables, or revealed in the startup command.
27+
$egg->variables()->first()->update([
28+
'user_viewable' => false,
29+
]);
30+
31+
$server->fill([
32+
'egg_id' => $egg->id,
33+
'startup' => 'java {{SERVER_JARFILE}} --version {{BUNGEE_VERSION}}',
34+
])->save();
35+
$server = $server->refresh();
36+
37+
$response = $this->actingAs($user)->getJson($this->link($server) . "/startup");
38+
39+
$response->assertOk();
40+
$response->assertJsonPath('meta.startup_command', 'java bungeecord.jar --version [hidden]');
41+
$response->assertJsonPath('meta.raw_startup_command', $server->startup);
42+
43+
$response->assertJsonPath('object', 'list');
44+
$response->assertJsonCount(1, 'data');
45+
$response->assertJsonPath('data.0.object', EggVariable::RESOURCE_NAME);
46+
$this->assertJsonTransformedWith($response->json('data.0.attributes'), $egg->variables[1]);
47+
}
48+
49+
/**
50+
* Test that a user without the required permission, or who does not have any permission to
51+
* access the server cannot get the startup information for it.
52+
*/
53+
public function testStartupDataIsNotReturnedWithoutPermission()
54+
{
55+
[$user, $server] = $this->generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT]);
56+
$this->actingAs($user)->getJson($this->link($server) . "/startup")->assertForbidden();
57+
58+
$user2 = factory(User::class)->create();
59+
$this->actingAs($user2)->getJson($this->link($server) . "/startup")->assertNotFound();
60+
}
61+
62+
/**
63+
* @return array[]
64+
*/
65+
public function permissionsDataProvider()
66+
{
67+
return [[[]], [[Permission::ACTION_STARTUP_READ]]];
68+
}
69+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<?php
2+
3+
namespace Pterodactyl\Tests\Integration\Api\Client\Server\Startup;
4+
5+
use Pterodactyl\Models\User;
6+
use Illuminate\Http\Response;
7+
use Pterodactyl\Models\Permission;
8+
use Pterodactyl\Models\EggVariable;
9+
use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
10+
11+
class UpdateStartupVariableTest extends ClientApiIntegrationTestCase
12+
{
13+
/**
14+
* Test that a startup variable can be edited successfully for a server.
15+
*
16+
* @param array $permissions
17+
* @dataProvider permissionsDataProvider
18+
*/
19+
public function testStartupVariableCanBeUpdated($permissions)
20+
{
21+
/** @var \Pterodactyl\Models\Server $server */
22+
[$user, $server] = $this->generateTestAccount($permissions);
23+
$server->fill([
24+
'startup' => 'java {{SERVER_JARFILE}} --version {{BUNGEE_VERSION}}',
25+
])->save();
26+
27+
$response = $this->actingAs($user)->putJson($this->link($server) . '/startup/variable', [
28+
'key' => 'BUNGEE_VERSION',
29+
'value' => '1.2.3',
30+
]);
31+
32+
$response->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY);
33+
$response->assertJsonPath('errors.0.code', 'ValidationException');
34+
$response->assertJsonPath('errors.0.detail', 'The value may only contain letters and numbers.');
35+
36+
$response = $this->actingAs($user)->putJson($this->link($server) . '/startup/variable', [
37+
'key' => 'BUNGEE_VERSION',
38+
'value' => '123',
39+
]);
40+
41+
$response->assertOk();
42+
$response->assertJsonPath('object', EggVariable::RESOURCE_NAME);
43+
$this->assertJsonTransformedWith($response->json('attributes'), $server->variables[0]);
44+
$response->assertJsonPath('meta.startup_command', 'java bungeecord.jar --version 123');
45+
$response->assertJsonPath('meta.raw_startup_command', $server->startup);
46+
}
47+
48+
/**
49+
* Test that variables that are either not user_viewable, or not user_editable, cannot be
50+
* updated via this endpoint.
51+
*
52+
* @param array $permissions
53+
* @dataProvider permissionsDataProvider
54+
*/
55+
public function testStartupVariableCannotBeUpdatedIfNotUserViewableOrEditable(array $permissions)
56+
{
57+
/** @var \Pterodactyl\Models\Server $server */
58+
[$user, $server] = $this->generateTestAccount($permissions);
59+
60+
$egg = $this->cloneEggAndVariables($server->egg);
61+
$egg->variables()->where('env_variable', 'BUNGEE_VERSION')->update(['user_viewable' => false]);
62+
$egg->variables()->where('env_variable', 'SERVER_JARFILE')->update(['user_editable' => false]);
63+
64+
$server->fill(['egg_id' => $egg->id])->save();
65+
$server->refresh();
66+
67+
$response = $this->actingAs($user)->putJson($this->link($server) . '/startup/variable', [
68+
'key' => 'BUNGEE_VERSION',
69+
'value' => '123',
70+
]);
71+
72+
$response->assertStatus(Response::HTTP_BAD_REQUEST);
73+
$response->assertJsonPath('errors.0.code', 'BadRequestHttpException');
74+
$response->assertJsonPath('errors.0.detail', 'The environment variable you are trying to edit does not exist.');
75+
76+
$response = $this->actingAs($user)->putJson($this->link($server) . '/startup/variable', [
77+
'key' => 'SERVER_JARFILE',
78+
'value' => 'server2.jar',
79+
]);
80+
81+
$response->assertStatus(Response::HTTP_BAD_REQUEST);
82+
$response->assertJsonPath('errors.0.code', 'BadRequestHttpException');
83+
$response->assertJsonPath('errors.0.detail', 'The environment variable you are trying to edit is read-only.');
84+
}
85+
86+
/**
87+
* Test that a hidden variable is not included in the startup_command output for the server if
88+
* a different variable is updated.
89+
*/
90+
public function testHiddenVariablesAreNotReturnedInStartupCommandWhenUpdatingVariable()
91+
{
92+
/** @var \Pterodactyl\Models\Server $server */
93+
[$user, $server] = $this->generateTestAccount();
94+
95+
$egg = $this->cloneEggAndVariables($server->egg);
96+
$egg->variables()->first()->update(['user_viewable' => false]);
97+
98+
$server->fill([
99+
'egg_id' => $egg->id,
100+
'startup' => 'java {{SERVER_JARFILE}} --version {{BUNGEE_VERSION}}',
101+
])->save();
102+
103+
$server->refresh();
104+
105+
$response = $this->actingAs($user)->putJson($this->link($server) . '/startup/variable', [
106+
'key' => 'SERVER_JARFILE',
107+
'value' => 'server2.jar',
108+
]);
109+
110+
$response->assertOk();
111+
$response->assertJsonPath('meta.startup_command', 'java server2.jar --version [hidden]');
112+
$response->assertJsonPath('meta.raw_startup_command', $server->startup);
113+
}
114+
115+
/**
116+
* Test that an egg variable with a validation rule of 'nullable|string' works if no value
117+
* is passed through in the request.
118+
*
119+
* @see https://github.com/pterodactyl/panel/issues/2433
120+
*/
121+
public function testEggVariableWithNullableStringIsNotRequired()
122+
{
123+
/** @var \Pterodactyl\Models\Server $server */
124+
[$user, $server] = $this->generateTestAccount();
125+
126+
$egg = $this->cloneEggAndVariables($server->egg);
127+
$egg->variables()->first()->update(['rules' => 'nullable|string']);
128+
129+
$server->fill(['egg_id' => $egg->id])->save();
130+
$server->refresh();
131+
132+
$response = $this->actingAs($user)->putJson($this->link($server) . '/startup/variable', [
133+
'key' => 'BUNGEE_VERSION',
134+
'value' => '',
135+
]);
136+
137+
$response->assertOk();
138+
$response->assertJsonPath('attributes.server_value', null);
139+
}
140+
141+
/**
142+
* Test that a variable cannot be updated if the user does not have permission to perform
143+
* that action, or they aren't assigned at all to the server.
144+
*/
145+
public function testStartupVariableCannotBeUpdatedIfNotUserViewable()
146+
{
147+
[$user, $server] = $this->generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT]);
148+
$this->actingAs($user)->putJson($this->link($server) . "/startup/variable")->assertForbidden();
149+
150+
$user2 = factory(User::class)->create();
151+
$this->actingAs($user2)->putJson($this->link($server) . "/startup/variable")->assertNotFound();
152+
}
153+
154+
/**
155+
* @return \array[][]
156+
*/
157+
public function permissionsDataProvider()
158+
{
159+
return [[[]], [[Permission::ACTION_STARTUP_UPDATE]]];
160+
}
161+
}

0 commit comments

Comments
 (0)