Path: blob/1.0-develop/tests/Unit/Http/Middleware/Api/Daemon/DaemonAuthenticateTest.php
7461 views
<?php12namespace Pterodactyl\Tests\Unit\Http\Middleware\Api\Daemon;34use Mockery as m;5use Mockery\MockInterface;6use Pterodactyl\Models\Node;7use Illuminate\Contracts\Encryption\Encrypter;8use Pterodactyl\Repositories\Eloquent\NodeRepository;9use Symfony\Component\HttpKernel\Exception\HttpException;10use Pterodactyl\Exceptions\Repository\RecordNotFoundException;11use Pterodactyl\Http\Middleware\Api\Daemon\DaemonAuthenticate;12use Pterodactyl\Tests\Unit\Http\Middleware\MiddlewareTestCase;13use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;14use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;1516class DaemonAuthenticateTest extends MiddlewareTestCase17{18private MockInterface $encrypter;1920private MockInterface $repository;2122/**23* Setup tests.24*/25public function setUp(): void26{27parent::setUp();2829$this->encrypter = m::mock(Encrypter::class);30$this->repository = m::mock(NodeRepository::class);31}3233/**34* Test that if we are accessing the daemon.configuration route this middleware is not35* applied in order to allow an unauthenticated request to use a token to grab data.36*/37public function testResponseShouldContinueIfRouteIsExempted()38{39$this->request->expects('route->getName')->withNoArgs()->andReturn('daemon.configuration');4041$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());42}4344/**45* Test that not passing in the bearer token will result in a HTTP/401 error with the46* proper response headers.47*/48public function testResponseShouldFailIfNoTokenIsProvided()49{50$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');51$this->request->expects('bearerToken')->withNoArgs()->andReturnNull();5253try {54$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());55} catch (HttpException $exception) {56$this->assertEquals(401, $exception->getStatusCode(), 'Assert that a status code of 401 is returned.');57$this->assertTrue(is_array($exception->getHeaders()), 'Assert that an array of headers is returned.');58$this->assertArrayHasKey('WWW-Authenticate', $exception->getHeaders(), 'Assert exception headers contains WWW-Authenticate.');59$this->assertEquals('Bearer', $exception->getHeaders()['WWW-Authenticate']);60}61}6263/**64* Test that passing in an invalid node daemon secret will result in a bad request65* exception being returned.66*/67#[\PHPUnit\Framework\Attributes\DataProvider('badTokenDataProvider')]68public function testResponseShouldFailIfTokenFormatIsIncorrect(string $token)69{70$this->expectException(BadRequestHttpException::class);7172$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');73$this->request->expects('bearerToken')->withNoArgs()->andReturn($token);7475$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());76}7778/**79* Test that an access denied error is returned if the node is valid but the token80* provided is not valid.81*/82public function testResponseShouldFailIfTokenIsNotValid()83{84$this->expectException(AccessDeniedHttpException::class);8586/** @var Node $model */87$model = Node::factory()->make();8889$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');90$this->request->expects('bearerToken')->withNoArgs()->andReturn($model->daemon_token_id . '.random_string_123');9192$this->repository->expects('findFirstWhere')->with(['daemon_token_id' => $model->daemon_token_id])->andReturn($model);93$this->encrypter->expects('decrypt')->with($model->daemon_token)->andReturns(decrypt($model->daemon_token));9495$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());96}9798/**99* Test that an access denied exception is returned if the node is not found using100* the token ID provided.101*/102public function testResponseShouldFailIfNodeIsNotFound()103{104$this->expectException(AccessDeniedHttpException::class);105106$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');107$this->request->expects('bearerToken')->withNoArgs()->andReturn('abcd1234.random_string_123');108109$this->repository->expects('findFirstWhere')->with(['daemon_token_id' => 'abcd1234'])->andThrow(RecordNotFoundException::class);110111$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());112}113114/**115* Test a successful middleware process.116*/117public function testSuccessfulMiddlewareProcess()118{119/** @var Node $model */120$model = Node::factory()->make();121122$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');123$this->request->expects('bearerToken')->withNoArgs()->andReturn($model->daemon_token_id . '.' . decrypt($model->daemon_token));124125$this->repository->expects('findFirstWhere')->with(['daemon_token_id' => $model->daemon_token_id])->andReturn($model);126$this->encrypter->expects('decrypt')->with($model->daemon_token)->andReturns(decrypt($model->daemon_token));127128$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());129$this->assertRequestHasAttribute('node');130$this->assertRequestAttributeEquals($model, 'node');131}132133/**134* Provides different tokens that should trigger a bad request exception due to135* their formatting.136*137* @return array|\string[][]138*/139public static function badTokenDataProvider(): array140{141return [142['foo'],143['foobar'],144['foo-bar'],145['foo.bar.baz'],146['.foo'],147['foo.'],148['foo..bar'],149];150}151152/**153* Return an instance of the middleware using mocked dependencies.154*/155private function getMiddleware(): DaemonAuthenticate156{157return new DaemonAuthenticate($this->encrypter, $this->repository);158}159}160161162