|
| 1 | +<?php |
| 2 | + |
| 3 | +namespace Pterodactyl\Tests\Integration\Api\Client\Server; |
| 4 | + |
| 5 | +use Carbon\Carbon; |
| 6 | +use Lcobucci\JWT\Parser; |
| 7 | +use Carbon\CarbonImmutable; |
| 8 | +use Illuminate\Http\Response; |
| 9 | +use Pterodactyl\Models\Permission; |
| 10 | +use Lcobucci\JWT\Signer\Hmac\Sha256; |
| 11 | +use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; |
| 12 | + |
| 13 | +class WebsocketControllerTest extends ClientApiIntegrationTestCase |
| 14 | +{ |
| 15 | + /** |
| 16 | + * Test that a subuser attempting to connect to the websocket recieves an error if they |
| 17 | + * do not explicitly have the permission. |
| 18 | + */ |
| 19 | + public function testSubuserWithoutWebsocketPermissionReceivesError() |
| 20 | + { |
| 21 | + [$user, $server] = $this->generateTestAccount([Permission::ACTION_CONTROL_RESTART]); |
| 22 | + |
| 23 | + $this->actingAs($user)->getJson("/api/client/servers/{$server->uuid}/websocket") |
| 24 | + ->assertStatus(Response::HTTP_FORBIDDEN) |
| 25 | + ->assertJsonPath('errors.0.code', 'HttpException') |
| 26 | + ->assertJsonPath('errors.0.detail', 'You do not have permission to connect to this server\'s websocket.'); |
| 27 | + } |
| 28 | + |
| 29 | + /** |
| 30 | + * Test that the expected permissions are returned for the server owner and that the JWT is |
| 31 | + * configured correctly. |
| 32 | + */ |
| 33 | + public function testJwtAndWebsocketUrlAreReturnedForServerOwner() |
| 34 | + { |
| 35 | + CarbonImmutable::setTestNow(Carbon::now()); |
| 36 | + |
| 37 | + /** @var \Pterodactyl\Models\User $user */ |
| 38 | + /** @var \Pterodactyl\Models\Server $server */ |
| 39 | + [$user, $server] = $this->generateTestAccount(); |
| 40 | + |
| 41 | + // Force the node to HTTPS since we want to confirm it gets transformed to wss:// in the URL. |
| 42 | + $server->node->scheme = 'https'; |
| 43 | + $server->node->save(); |
| 44 | + |
| 45 | + $response = $this->actingAs($user)->getJson("/api/client/servers/{$server->uuid}/websocket"); |
| 46 | + |
| 47 | + $response->assertOk(); |
| 48 | + $response->assertJsonStructure(['data' => ['token', 'socket']]); |
| 49 | + |
| 50 | + $connection = $response->json('data.socket'); |
| 51 | + $this->assertStringStartsWith('wss://', $connection, 'Failed asserting that websocket connection address has expected "wss://" prefix.'); |
| 52 | + $this->assertStringEndsWith("/api/servers/{$server->uuid}/ws", $connection, 'Failed asserting that websocket connection address uses expected Wings endpoint.'); |
| 53 | + |
| 54 | + $token = (new Parser)->parse($response->json('data.token')); |
| 55 | + |
| 56 | + $this->assertTrue( |
| 57 | + $token->verify(new Sha256, $server->node->getDecryptedKey()), |
| 58 | + 'Failed to validate that the JWT data returned was signed using the Node\'s secret key.' |
| 59 | + ); |
| 60 | + |
| 61 | + // Check that the claims are generated correctly. |
| 62 | + $this->assertSame(config('app.url'), $token->getClaim('iss')); |
| 63 | + $this->assertSame($server->node->getConnectionAddress(), $token->getClaim('aud')); |
| 64 | + $this->assertSame(CarbonImmutable::now()->getTimestamp(), $token->getClaim('iat')); |
| 65 | + $this->assertSame(CarbonImmutable::now()->subMinutes(5)->getTimestamp(), $token->getClaim('nbf')); |
| 66 | + $this->assertSame(CarbonImmutable::now()->addMinutes(15)->getTimestamp(), $token->getClaim('exp')); |
| 67 | + $this->assertSame($user->id, $token->getClaim('user_id')); |
| 68 | + $this->assertSame($server->uuid, $token->getClaim('server_uuid')); |
| 69 | + $this->assertSame(['*'], $token->getClaim('permissions')); |
| 70 | + } |
| 71 | + |
| 72 | + /** |
| 73 | + * Test that the subuser's permissions are passed along correctly in the generated JWT. |
| 74 | + */ |
| 75 | + public function testJwtIsConfiguredCorrectlyForServerSubuser() |
| 76 | + { |
| 77 | + $permissions = [Permission::ACTION_WEBSOCKET_CONNECT, Permission::ACTION_CONTROL_CONSOLE]; |
| 78 | + |
| 79 | + /** @var \Pterodactyl\Models\User $user */ |
| 80 | + /** @var \Pterodactyl\Models\Server $server */ |
| 81 | + [$user, $server] = $this->generateTestAccount($permissions); |
| 82 | + |
| 83 | + $response = $this->actingAs($user)->getJson("/api/client/servers/{$server->uuid}/websocket"); |
| 84 | + |
| 85 | + $response->assertOk(); |
| 86 | + $response->assertJsonStructure(['data' => ['token', 'socket']]); |
| 87 | + |
| 88 | + $token = (new Parser)->parse($response->json('data.token')); |
| 89 | + |
| 90 | + $this->assertTrue( |
| 91 | + $token->verify(new Sha256, $server->node->getDecryptedKey()), |
| 92 | + 'Failed to validate that the JWT data returned was signed using the Node\'s secret key.' |
| 93 | + ); |
| 94 | + |
| 95 | + // Check that the claims are generated correctly. |
| 96 | + $this->assertSame($permissions, $token->getClaim('permissions')); |
| 97 | + } |
| 98 | +} |
0 commit comments