Skip to content

Commit 7224ca8

Browse files
committed
Fix bug preventing the creation of API keys with CIDR ranges
1 parent d47a058 commit 7224ca8

File tree

4 files changed

+213
-77
lines changed

4 files changed

+213
-77
lines changed

app/Http/Requests/Api/Client/Account/StoreApiKeyRequest.php

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
namespace Pterodactyl\Http\Requests\Api\Client\Account;
44

5+
use IPTools\Range;
56
use Pterodactyl\Models\ApiKey;
7+
use Illuminate\Validation\Validator;
68
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
79

810
class StoreApiKeyRequest extends ClientApiRequest
@@ -13,18 +15,33 @@ public function rules(): array
1315

1416
return [
1517
'description' => $rules['memo'],
16-
'allowed_ips' => $rules['allowed_ips'],
17-
'allowed_ips.*' => 'ip',
18+
'allowed_ips' => [...$rules['allowed_ips'], 'max:50'],
19+
'allowed_ips.*' => 'string',
1820
];
1921
}
2022

2123
/**
22-
* @return array|string[]
24+
* Check that each of the values entered is actually valid.
2325
*/
24-
public function messages()
26+
public function withValidator(Validator $validator): void
2527
{
26-
return [
27-
'allowed_ips.*' => 'All of the IP addresses entered must be valid IPv4 addresses.',
28-
];
28+
$validator->after(function (Validator $validator) {
29+
if (!is_array($ips = $this->input('allowed_ips'))) {
30+
return;
31+
}
32+
33+
foreach ($ips as $index => $ip) {
34+
$valid = false;
35+
try {
36+
$valid = Range::parse($ip)->valid();
37+
} catch (\Exception $exception) {
38+
if ($exception->getMessage() !== 'Invalid IP address format') {
39+
throw $exception;
40+
}
41+
} finally {
42+
$validator->errors()->addIf(!$valid, "allowed_ips.{$index}", '"' . $ip . '" is not a valid IP address or CIDR range.');
43+
}
44+
}
45+
});
2946
}
3047
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
namespace Pterodactyl\Tests\Assertions;
4+
5+
use PHPUnit\Framework\Assert;
6+
use Illuminate\Support\Facades\Event;
7+
use Pterodactyl\Events\ActivityLogged;
8+
use Illuminate\Database\Eloquent\Model;
9+
use Pterodactyl\Models\ActivityLogSubject;
10+
11+
trait AssertsActivityLogged
12+
{
13+
/**
14+
* @param \Illuminate\Database\Eloquent\Model|array $subjects
15+
*/
16+
public function assertActivityFor(string $event, ?Model $actor, ...$subjects): void
17+
{
18+
$this->assertActivityLogged($event);
19+
$this->assertActivityActor($event, $actor);
20+
$this->assertActivitySubjects($event, ...$subjects);
21+
}
22+
23+
/**
24+
* Asserts that the given activity log event was stored in the database.
25+
*/
26+
public function assertActivityLogged(string $event): void
27+
{
28+
Event::assertDispatched(ActivityLogged::class, fn ($e) => $e->is($event));
29+
}
30+
31+
/**
32+
* Asserts that a given activity log event was stored with the subjects being
33+
* any of the values provided.
34+
*
35+
* @param \Illuminate\Database\Eloquent\Model|array $subjects
36+
*/
37+
public function assertActivitySubjects(string $event, $subjects): void
38+
{
39+
if (is_array($subjects)) {
40+
\Webmozart\Assert\Assert::lessThanEq(count(func_get_args()), 2, 'Invalid call to ' . __METHOD__ . ': cannot provide additional arguments if providing an array.');
41+
} else {
42+
$subjects = array_slice(func_get_args(), 1);
43+
}
44+
45+
Event::assertDispatched(ActivityLogged::class, function (ActivityLogged $e) use ($event, $subjects) {
46+
Assert::assertEquals($event, $e->model->event);
47+
Assert::assertNotEmpty($e->model->subjects);
48+
49+
foreach ($subjects as $subject) {
50+
$match = $e->model->subjects->first(function (ActivityLogSubject $model) use ($subject) {
51+
return $model->subject_type === $subject->getMorphClass()
52+
&& $model->subject_id = $subject->getKey();
53+
});
54+
55+
Assert::assertNotNull(
56+
$match,
57+
sprintf('Failed asserting that event "%s" includes a %s[%d] subject', $event, get_class($subject), $subject->getKey())
58+
);
59+
}
60+
61+
return true;
62+
});
63+
}
64+
65+
/**
66+
* Asserts that the provided event was logged into the activity logs with the provided
67+
* actor model associated with it.
68+
*/
69+
public function assertActivityActor(string $event, ?Model $actor = null): void
70+
{
71+
Event::assertDispatched(ActivityLogged::class, function (ActivityLogged $e) use ($event, $actor) {
72+
Assert::assertEquals($event, $e->model->event);
73+
74+
if (is_null($actor)) {
75+
Assert::assertNull($e->actor());
76+
} else {
77+
Assert::assertNotNull($e->actor());
78+
Assert::assertTrue($e->actor()->is($actor));
79+
}
80+
81+
return true;
82+
});
83+
}
84+
}

0 commit comments

Comments
 (0)