Skip to content

Commit d844a36

Browse files
committed
Begin adding unit tests for middleware
1 parent e9aecfe commit d844a36

File tree

4 files changed

+324
-20
lines changed

4 files changed

+324
-20
lines changed

app/Http/Middleware/Daemon/DaemonAuthenticate.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,20 @@
3232

3333
class DaemonAuthenticate
3434
{
35+
/**
36+
* @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface
37+
*/
38+
private $repository;
39+
3540
/**
3641
* Daemon routes that this middleware should be skipped on.
42+
*
3743
* @var array
3844
*/
3945
protected $except = [
4046
'daemon.configuration',
4147
];
4248

43-
/**
44-
* @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface
45-
*/
46-
protected $repository;
47-
4849
/**
4950
* DaemonAuthenticate constructor.
5051
*

app/Http/Middleware/Server/AccessingValidServer.php

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
use Illuminate\Http\Request;
77
use Pterodactyl\Models\Server;
88
use Illuminate\Contracts\Session\Session;
9-
use Illuminate\Auth\AuthenticationException;
109
use Illuminate\Contracts\Config\Repository as ConfigRepository;
1110
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
1211
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -17,22 +16,17 @@ class AccessingValidServer
1716
/**
1817
* @var \Illuminate\Contracts\Config\Repository
1918
*/
20-
protected $config;
19+
private $config;
2120

2221
/**
2322
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
2423
*/
25-
protected $repository;
26-
27-
/**
28-
* @var \Pterodactyl\Models\Server
29-
*/
30-
protected $server;
24+
private $repository;
3125

3226
/**
3327
* @var \Illuminate\Contracts\Session\Session
3428
*/
35-
protected $session;
29+
private $session;
3630

3731
/**
3832
* AccessingValidServer constructor.
@@ -56,7 +50,7 @@ public function __construct(
5650
*
5751
* @param \Illuminate\Http\Request $request
5852
* @param \Closure $next
59-
* @return mixed
53+
* @return \Illuminate\Http\Response|mixed
6054
*
6155
* @throws \Illuminate\Auth\AuthenticationException
6256
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
@@ -65,10 +59,6 @@ public function __construct(
6559
*/
6660
public function handle(Request $request, Closure $next)
6761
{
68-
if (! $request->user()) {
69-
throw new AuthenticationException;
70-
}
71-
7262
$attributes = $request->route()->parameter('server');
7363
$isApiRequest = $request->expectsJson() || $request->is(...$this->config->get('pterodactyl.json_routes', []));
7464
$server = $this->repository->getByUuid($attributes instanceof Server ? $attributes->uuid : $attributes);
@@ -89,9 +79,11 @@ public function handle(Request $request, Closure $next)
8979
return response()->view('errors.suspended', [], 403);
9080
}
9181

82+
// Servers can have install statuses other than 1 or 0, so don't check
83+
// for a bool-type operator here.
9284
if ($server->installed !== 1) {
9385
if ($isApiRequest) {
94-
throw new AccessDeniedHttpException('Server is completing install process.');
86+
throw new AccessDeniedHttpException('Server is not marked as installed.');
9587
}
9688

9789
return response()->view('errors.installing', [], 403);
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
namespace Tests\Unit\Http\Middleware\Daemon;
4+
5+
use Closure;
6+
use Mockery as m;
7+
use Tests\TestCase;
8+
use Illuminate\Http\Request;
9+
use Pterodactyl\Models\Node;
10+
use Symfony\Component\HttpFoundation\ParameterBag;
11+
use Symfony\Component\HttpKernel\Exception\HttpException;
12+
use Pterodactyl\Http\Middleware\Daemon\DaemonAuthenticate;
13+
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
14+
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
15+
16+
class DaemonAuthenticateTest extends TestCase
17+
{
18+
/**
19+
* @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface|\Mockery\Mock
20+
*/
21+
private $repository;
22+
23+
/**
24+
* @var \Illuminate\Http\Request|\Mockery\Mock
25+
*/
26+
private $request;
27+
28+
/**
29+
* Setup tests.
30+
*/
31+
public function setUp()
32+
{
33+
parent::setUp();
34+
35+
$this->repository = m::mock(NodeRepositoryInterface::class);
36+
$this->request = m::mock(Request::class);
37+
$this->request->attributes = new ParameterBag();
38+
}
39+
40+
/**
41+
* Test that if we are accessing the daemon.configuration route this middleware is not
42+
* applied in order to allow an unauthenticated request to use a token to grab data.
43+
*/
44+
public function testResponseShouldContinueIfRouteIsExempted()
45+
{
46+
$this->request->shouldReceive('route->getName')->withNoArgs()->once()->andReturn('daemon.configuration');
47+
48+
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
49+
}
50+
51+
/**
52+
* Test that not passing in the bearer token will result in a HTTP/401 error with the
53+
* proper response headers.
54+
*/
55+
public function testResponseShouldFailIfNoTokenIsProvided()
56+
{
57+
$this->request->shouldReceive('route->getName')->withNoArgs()->once()->andReturn('random.route');
58+
$this->request->shouldReceive('bearerToken')->withNoArgs()->once()->andReturnNull();
59+
60+
try {
61+
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
62+
} catch (HttpException $exception) {
63+
$this->assertEquals(401, $exception->getStatusCode(), 'Assert that a status code of 401 is returned.');
64+
$this->assertTrue(is_array($exception->getHeaders()), 'Assert that an array of headers is returned.');
65+
$this->assertArrayHasKey('WWW-Authenticate', $exception->getHeaders(), 'Assert exception headers contains WWW-Authenticate.');
66+
$this->assertEquals('Bearer', $exception->getHeaders()['WWW-Authenticate']);
67+
}
68+
}
69+
70+
/**
71+
* Test that passing in an invalid node daemon secret will result in a HTTP/403
72+
* error response.
73+
*/
74+
public function testResponseShouldFailIfNoNodeIsFound()
75+
{
76+
$this->request->shouldReceive('route->getName')->withNoArgs()->once()->andReturn('random.route');
77+
$this->request->shouldReceive('bearerToken')->withNoArgs()->once()->andReturn('test1234');
78+
79+
$this->repository->shouldReceive('findFirstWhere')->with([['daemonSecret', '=', 'test1234']])->once()->andThrow(new RecordNotFoundException);
80+
81+
try {
82+
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
83+
} catch (HttpException $exception) {
84+
$this->assertEquals(403, $exception->getStatusCode(), 'Assert that a status code of 403 is returned.');
85+
}
86+
}
87+
88+
/**
89+
* Test a successful middleware process.
90+
*/
91+
public function testSuccessfulMiddlewareProcess()
92+
{
93+
$model = factory(Node::class)->make();
94+
95+
$this->request->shouldReceive('route->getName')->withNoArgs()->once()->andReturn('random.route');
96+
$this->request->shouldReceive('bearerToken')->withNoArgs()->once()->andReturn($model->daemonSecret);
97+
98+
$this->repository->shouldReceive('findFirstWhere')->with([['daemonSecret', '=', $model->daemonSecret]])->once()->andReturn($model);
99+
100+
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
101+
$this->assertTrue($this->request->attributes->has('node'), 'Assert request attributes contains node.');
102+
$this->assertSame($model, $this->request->attributes->get('node'));
103+
}
104+
105+
/**
106+
* Return an instance of the middleware using mocked dependencies.
107+
*
108+
* @return \Pterodactyl\Http\Middleware\Daemon\DaemonAuthenticate
109+
*/
110+
private function getMiddleware(): DaemonAuthenticate
111+
{
112+
return new DaemonAuthenticate($this->repository);
113+
}
114+
115+
/**
116+
* Provide a closure to be used when validating that the response from the middleware
117+
* is the same request object we passed into it.
118+
*/
119+
private function getClosureAssertions(): Closure
120+
{
121+
return function ($response) {
122+
$this->assertInstanceOf(Request::class, $response);
123+
$this->assertSame($this->request, $response);
124+
};
125+
}
126+
}

0 commit comments

Comments
 (0)