Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pterodactyl
GitHub Repository: pterodactyl/panel
Path: blob/1.0-develop/tests/Unit/Http/Middleware/Api/Daemon/DaemonAuthenticateTest.php
7461 views
1
<?php
2
3
namespace Pterodactyl\Tests\Unit\Http\Middleware\Api\Daemon;
4
5
use Mockery as m;
6
use Mockery\MockInterface;
7
use Pterodactyl\Models\Node;
8
use Illuminate\Contracts\Encryption\Encrypter;
9
use Pterodactyl\Repositories\Eloquent\NodeRepository;
10
use Symfony\Component\HttpKernel\Exception\HttpException;
11
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
12
use Pterodactyl\Http\Middleware\Api\Daemon\DaemonAuthenticate;
13
use Pterodactyl\Tests\Unit\Http\Middleware\MiddlewareTestCase;
14
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
15
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
16
17
class DaemonAuthenticateTest extends MiddlewareTestCase
18
{
19
private MockInterface $encrypter;
20
21
private MockInterface $repository;
22
23
/**
24
* Setup tests.
25
*/
26
public function setUp(): void
27
{
28
parent::setUp();
29
30
$this->encrypter = m::mock(Encrypter::class);
31
$this->repository = m::mock(NodeRepository::class);
32
}
33
34
/**
35
* Test that if we are accessing the daemon.configuration route this middleware is not
36
* applied in order to allow an unauthenticated request to use a token to grab data.
37
*/
38
public function testResponseShouldContinueIfRouteIsExempted()
39
{
40
$this->request->expects('route->getName')->withNoArgs()->andReturn('daemon.configuration');
41
42
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
43
}
44
45
/**
46
* Test that not passing in the bearer token will result in a HTTP/401 error with the
47
* proper response headers.
48
*/
49
public function testResponseShouldFailIfNoTokenIsProvided()
50
{
51
$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');
52
$this->request->expects('bearerToken')->withNoArgs()->andReturnNull();
53
54
try {
55
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
56
} catch (HttpException $exception) {
57
$this->assertEquals(401, $exception->getStatusCode(), 'Assert that a status code of 401 is returned.');
58
$this->assertTrue(is_array($exception->getHeaders()), 'Assert that an array of headers is returned.');
59
$this->assertArrayHasKey('WWW-Authenticate', $exception->getHeaders(), 'Assert exception headers contains WWW-Authenticate.');
60
$this->assertEquals('Bearer', $exception->getHeaders()['WWW-Authenticate']);
61
}
62
}
63
64
/**
65
* Test that passing in an invalid node daemon secret will result in a bad request
66
* exception being returned.
67
*/
68
#[\PHPUnit\Framework\Attributes\DataProvider('badTokenDataProvider')]
69
public function testResponseShouldFailIfTokenFormatIsIncorrect(string $token)
70
{
71
$this->expectException(BadRequestHttpException::class);
72
73
$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');
74
$this->request->expects('bearerToken')->withNoArgs()->andReturn($token);
75
76
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
77
}
78
79
/**
80
* Test that an access denied error is returned if the node is valid but the token
81
* provided is not valid.
82
*/
83
public function testResponseShouldFailIfTokenIsNotValid()
84
{
85
$this->expectException(AccessDeniedHttpException::class);
86
87
/** @var Node $model */
88
$model = Node::factory()->make();
89
90
$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');
91
$this->request->expects('bearerToken')->withNoArgs()->andReturn($model->daemon_token_id . '.random_string_123');
92
93
$this->repository->expects('findFirstWhere')->with(['daemon_token_id' => $model->daemon_token_id])->andReturn($model);
94
$this->encrypter->expects('decrypt')->with($model->daemon_token)->andReturns(decrypt($model->daemon_token));
95
96
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
97
}
98
99
/**
100
* Test that an access denied exception is returned if the node is not found using
101
* the token ID provided.
102
*/
103
public function testResponseShouldFailIfNodeIsNotFound()
104
{
105
$this->expectException(AccessDeniedHttpException::class);
106
107
$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');
108
$this->request->expects('bearerToken')->withNoArgs()->andReturn('abcd1234.random_string_123');
109
110
$this->repository->expects('findFirstWhere')->with(['daemon_token_id' => 'abcd1234'])->andThrow(RecordNotFoundException::class);
111
112
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
113
}
114
115
/**
116
* Test a successful middleware process.
117
*/
118
public function testSuccessfulMiddlewareProcess()
119
{
120
/** @var Node $model */
121
$model = Node::factory()->make();
122
123
$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');
124
$this->request->expects('bearerToken')->withNoArgs()->andReturn($model->daemon_token_id . '.' . decrypt($model->daemon_token));
125
126
$this->repository->expects('findFirstWhere')->with(['daemon_token_id' => $model->daemon_token_id])->andReturn($model);
127
$this->encrypter->expects('decrypt')->with($model->daemon_token)->andReturns(decrypt($model->daemon_token));
128
129
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
130
$this->assertRequestHasAttribute('node');
131
$this->assertRequestAttributeEquals($model, 'node');
132
}
133
134
/**
135
* Provides different tokens that should trigger a bad request exception due to
136
* their formatting.
137
*
138
* @return array|\string[][]
139
*/
140
public static function badTokenDataProvider(): array
141
{
142
return [
143
['foo'],
144
['foobar'],
145
['foo-bar'],
146
['foo.bar.baz'],
147
['.foo'],
148
['foo.'],
149
['foo..bar'],
150
];
151
}
152
153
/**
154
* Return an instance of the middleware using mocked dependencies.
155
*/
156
private function getMiddleware(): DaemonAuthenticate
157
{
158
return new DaemonAuthenticate($this->encrypter, $this->repository);
159
}
160
}
161
162