auth = m::mock(AuthManager::class); $this->encrypter = m::mock(Encrypter::class); $this->repository = m::mock(ApiKeyRepositoryInterface::class); } /** * Test that a missing bearer token will throw an exception. */ public function testMissingBearerTokenThrowsException() { $this->request->shouldReceive('user')->andReturnNull(); $this->request->shouldReceive('bearerToken')->withNoArgs()->once()->andReturnNull(); try { $this->getMiddleware()->handle($this->request, $this->getClosureAssertions(), ApiKey::TYPE_APPLICATION); } catch (HttpException $exception) { $this->assertEquals(401, $exception->getStatusCode()); $this->assertEquals(['WWW-Authenticate' => 'Bearer'], $exception->getHeaders()); } } /** * Test that an invalid API identifier throws an exception. */ public function testInvalidIdentifier() { $this->expectException(AccessDeniedHttpException::class); $this->request->shouldReceive('bearerToken')->withNoArgs()->twice()->andReturn('abcd1234'); $this->repository->shouldReceive('findFirstWhere')->andThrow(new RecordNotFoundException); $this->getMiddleware()->handle($this->request, $this->getClosureAssertions(), ApiKey::TYPE_APPLICATION); } /** * Test that a valid token can continue past the middleware. */ public function testValidToken() { $model = ApiKey::factory()->make(); $this->request->shouldReceive('bearerToken')->withNoArgs()->twice()->andReturn($model->identifier . 'decrypted'); $this->repository->shouldReceive('findFirstWhere')->with([ ['identifier', '=', $model->identifier], ['key_type', '=', ApiKey::TYPE_APPLICATION], ])->once()->andReturn($model); $this->encrypter->shouldReceive('decrypt')->with($model->token)->once()->andReturn('decrypted'); $this->auth->shouldReceive('guard->loginUsingId')->with($model->user_id)->once()->andReturnNull(); $this->repository->shouldReceive('withoutFreshModel->update')->with($model->id, [ 'last_used_at' => CarbonImmutable::now(), ])->once()->andReturnNull(); $this->getMiddleware()->handle($this->request, $this->getClosureAssertions(), ApiKey::TYPE_APPLICATION); $this->assertEquals($model, $this->request->attributes->get('api_key')); } /** * Test that a valid token can continue past the middleware when set as a user token. */ public function testValidTokenWithUserKey() { $model = ApiKey::factory()->make(); $this->request->shouldReceive('bearerToken')->withNoArgs()->twice()->andReturn($model->identifier . 'decrypted'); $this->repository->shouldReceive('findFirstWhere')->with([ ['identifier', '=', $model->identifier], ['key_type', '=', ApiKey::TYPE_ACCOUNT], ])->once()->andReturn($model); $this->encrypter->shouldReceive('decrypt')->with($model->token)->once()->andReturn('decrypted'); $this->auth->shouldReceive('guard->loginUsingId')->with($model->user_id)->once()->andReturnNull(); $this->repository->shouldReceive('withoutFreshModel->update')->with($model->id, [ 'last_used_at' => CarbonImmutable::now(), ])->once()->andReturnNull(); $this->getMiddleware()->handle($this->request, $this->getClosureAssertions(), ApiKey::TYPE_ACCOUNT); $this->assertEquals($model, $this->request->attributes->get('api_key')); } /** * Test that we can still make it though this middleware if the user is logged in and passing * through a cookie. */ public function testAccessWithoutToken() { $user = User::factory()->make(['id' => 123]); $this->request->shouldReceive('user')->andReturn($user); $this->request->shouldReceive('bearerToken')->withNoArgs()->twice()->andReturnNull(); $this->getMiddleware()->handle($this->request, $this->getClosureAssertions(), ApiKey::TYPE_ACCOUNT); $model = $this->request->attributes->get('api_key'); $this->assertSame(ApiKey::TYPE_ACCOUNT, $model->key_type); $this->assertSame(123, $model->user_id); $this->assertNull($model->identifier); } /** * Test that a valid token identifier with an invalid token attached to it * triggers an exception. */ public function testInvalidTokenForIdentifier() { $this->expectException(AccessDeniedHttpException::class); $model = ApiKey::factory()->make(); $this->request->shouldReceive('bearerToken')->withNoArgs()->twice()->andReturn($model->identifier . 'asdf'); $this->repository->shouldReceive('findFirstWhere')->with([ ['identifier', '=', $model->identifier], ['key_type', '=', ApiKey::TYPE_APPLICATION], ])->once()->andReturn($model); $this->encrypter->shouldReceive('decrypt')->with($model->token)->once()->andReturn('decrypted'); $this->getMiddleware()->handle($this->request, $this->getClosureAssertions(), ApiKey::TYPE_APPLICATION); } /** * Return an instance of the middleware with mocked dependencies for testing. * * @return \Pterodactyl\Http\Middleware\Api\AuthenticateKey */ private function getMiddleware(): AuthenticateKey { return new AuthenticateKey($this->repository, $this->auth, $this->encrypter); } }