Merge branch 'develop' into dane/restore-backups

This commit is contained in:
Dane Everitt 2021-01-25 19:16:40 -08:00
commit 663143de0b
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
575 changed files with 6080 additions and 6864 deletions

View file

@ -0,0 +1,60 @@
<?php
namespace Pterodactyl\Tests\Integration\Api\Client\Server\Allocation;
use Pterodactyl\Models\Subuser;
use Pterodactyl\Models\Allocation;
use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
class AllocationAuthorizationTest extends ClientApiIntegrationTestCase
{
/**
* @dataProvider methodDataProvider
*/
public function testAccessToAServersAllocationsIsRestrictedProperly(string $method, string $endpoint)
{
// The API $user is the owner of $server1.
[$user, $server1] = $this->generateTestAccount();
// Will be a subuser of $server2.
$server2 = $this->createServerModel();
// And as no access to $server3.
$server3 = $this->createServerModel();
// Set the API $user as a subuser of server 2, but with no permissions
// to do anything with the allocations for that server.
Subuser::factory()->create(['server_id' => $server2->id, 'user_id' => $user->id]);
$allocation1 = Allocation::factory()->create(['server_id' => $server1->id, 'node_id' => $server1->node_id]);
$allocation2 = Allocation::factory()->create(['server_id' => $server2->id, 'node_id' => $server2->node_id]);
$allocation3 = Allocation::factory()->create(['server_id' => $server3->id, 'node_id' => $server3->node_id]);
// This is the only valid call for this test, accessing the allocation for the same
// server that the API user is the owner of.
$response = $this->actingAs($user)->json($method, $this->link($server1, '/network/allocations/' . $allocation1->id . $endpoint));
$this->assertTrue($response->status() <= 204 || $response->status() === 400 || $response->status() === 422);
// This request fails because the allocation is valid for that server but the user
// making the request is not authorized to perform that action.
$this->actingAs($user)->json($method, $this->link($server2, '/network/allocations/' . $allocation2->id . $endpoint))->assertForbidden();
// Both of these should report a 404 error due to the allocations being linked to
// servers that are not the same as the server in the request, or are assigned
// to a server for which the user making the request has no access to.
$this->actingAs($user)->json($method, $this->link($server1, '/network/allocations/' . $allocation2->id . $endpoint))->assertNotFound();
$this->actingAs($user)->json($method, $this->link($server1, '/network/allocations/' . $allocation3->id . $endpoint))->assertNotFound();
$this->actingAs($user)->json($method, $this->link($server2, '/network/allocations/' . $allocation3->id . $endpoint))->assertNotFound();
$this->actingAs($user)->json($method, $this->link($server3, '/network/allocations/' . $allocation3->id . $endpoint))->assertNotFound();
}
/**
* @return \string[][]
*/
public function methodDataProvider(): array
{
return [
['POST', ''],
['DELETE', ''],
['POST', '/primary'],
];
}
}

View file

@ -3,8 +3,8 @@
namespace Pterodactyl\Tests\Integration\Api\Client\Server\Allocation;
use Illuminate\Http\Response;
use Pterodactyl\Models\Permission;
use Pterodactyl\Models\Allocation;
use Pterodactyl\Models\Permission;
use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
class CreateNewAllocationTest extends ClientApiIntegrationTestCase
@ -24,7 +24,6 @@ class CreateNewAllocationTest extends ClientApiIntegrationTestCase
/**
* Tests that a new allocation can be properly assigned to a server.
*
* @param array $permission
* @dataProvider permissionDataProvider
*/
public function testNewAllocationCanBeAssignedToServer(array $permission)
@ -33,7 +32,7 @@ class CreateNewAllocationTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount($permission);
$server->update(['allocation_limit' => 2]);
$response = $this->actingAs($user)->postJson($this->link($server, "/network/allocations"));
$response = $this->actingAs($user)->postJson($this->link($server, '/network/allocations'));
$response->assertJsonPath('object', Allocation::RESOURCE_NAME);
$matched = Allocation::query()->findOrFail($response->json('attributes.id'));
@ -52,7 +51,7 @@ class CreateNewAllocationTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount([Permission::ACTION_ALLOCATION_UPDATE]);
$server->update(['allocation_limit' => 2]);
$this->actingAs($user)->postJson($this->link($server, "/network/allocations"))->assertForbidden();
$this->actingAs($user)->postJson($this->link($server, '/network/allocations'))->assertForbidden();
}
/**
@ -66,7 +65,7 @@ class CreateNewAllocationTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount();
$server->update(['allocation_limit' => 2]);
$this->actingAs($user)->postJson($this->link($server, "/network/allocations"))
$this->actingAs($user)->postJson($this->link($server, '/network/allocations'))
->assertStatus(Response::HTTP_BAD_REQUEST)
->assertJsonPath('errors.0.code', 'AutoAllocationNotEnabledException')
->assertJsonPath('errors.0.detail', 'Server auto-allocation is not enabled for this instance.');
@ -81,7 +80,7 @@ class CreateNewAllocationTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount();
$server->update(['allocation_limit' => 1]);
$this->actingAs($user)->postJson($this->link($server, "/network/allocations"))
$this->actingAs($user)->postJson($this->link($server, '/network/allocations'))
->assertStatus(Response::HTTP_BAD_REQUEST)
->assertJsonPath('errors.0.code', 'DisplayException')
->assertJsonPath('errors.0.detail', 'Cannot assign additional allocations to this server: limit has been reached.');

View file

@ -3,8 +3,8 @@
namespace Pterodactyl\Tests\Integration\Api\Client\Server\Allocation;
use Illuminate\Http\Response;
use Pterodactyl\Models\Permission;
use Pterodactyl\Models\Allocation;
use Pterodactyl\Models\Permission;
use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
class DeleteAllocationTest extends ClientApiIntegrationTestCase
@ -13,7 +13,6 @@ class DeleteAllocationTest extends ClientApiIntegrationTestCase
* Test that an allocation is deleted from the server and the notes are properly reset
* to an empty value on assignment.
*
* @param array $permission
* @dataProvider permissionDataProvider
*/
public function testAllocationCanBeDeletedFromServer(array $permission)
@ -22,7 +21,7 @@ class DeleteAllocationTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount($permission);
/** @var \Pterodactyl\Models\Allocation $allocation */
$allocation = factory(Allocation::class)->create([
$allocation = Allocation::factory()->create([
'server_id' => $server->id,
'node_id' => $server->node_id,
'notes' => 'hodor',
@ -42,7 +41,7 @@ class DeleteAllocationTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount([Permission::ACTION_ALLOCATION_CREATE]);
/** @var \Pterodactyl\Models\Allocation $allocation */
$allocation = factory(Allocation::class)->create([
$allocation = Allocation::factory()->create([
'server_id' => $server->id,
'node_id' => $server->node_id,
'notes' => 'hodor',

View file

@ -0,0 +1,69 @@
<?php
namespace Pterodactyl\Tests\Integration\Api\Client\Server\Backup;
use Mockery;
use Carbon\CarbonImmutable;
use Pterodactyl\Models\Backup;
use Pterodactyl\Models\Subuser;
use Pterodactyl\Services\Backups\DeleteBackupService;
use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
class BackupAuthorizationTest extends ClientApiIntegrationTestCase
{
/**
* @dataProvider methodDataProvider
*/
public function testAccessToAServersBackupIsRestrictedProperly(string $method, string $endpoint)
{
// The API $user is the owner of $server1.
[$user, $server1] = $this->generateTestAccount();
// Will be a subuser of $server2.
$server2 = $this->createServerModel();
// And as no access to $server3.
$server3 = $this->createServerModel();
// Set the API $user as a subuser of server 2, but with no permissions
// to do anything with the backups for that server.
Subuser::factory()->create(['server_id' => $server2->id, 'user_id' => $user->id]);
$backup1 = Backup::factory()->create(['server_id' => $server1->id, 'completed_at' => CarbonImmutable::now()]);
$backup2 = Backup::factory()->create(['server_id' => $server2->id, 'completed_at' => CarbonImmutable::now()]);
$backup3 = Backup::factory()->create(['server_id' => $server3->id, 'completed_at' => CarbonImmutable::now()]);
$this->instance(DeleteBackupService::class, $mock = Mockery::mock(DeleteBackupService::class));
if ($method === 'DELETE') {
$mock->expects('handle')->andReturnUndefined();
}
// This is the only valid call for this test, accessing the backup for the same
// server that the API user is the owner of.
$this->actingAs($user)->json($method, $this->link($server1, '/backups/' . $backup1->uuid . $endpoint))
->assertStatus($method === 'DELETE' ? 204 : 200);
// This request fails because the backup is valid for that server but the user
// making the request is not authorized to perform that action.
$this->actingAs($user)->json($method, $this->link($server2, '/backups/' . $backup2->uuid . $endpoint))->assertForbidden();
// Both of these should report a 404 error due to the backup being linked to
// servers that are not the same as the server in the request, or are assigned
// to a server for which the user making the request has no access to.
$this->actingAs($user)->json($method, $this->link($server1, '/backups/' . $backup2->uuid . $endpoint))->assertNotFound();
$this->actingAs($user)->json($method, $this->link($server1, '/backups/' . $backup3->uuid . $endpoint))->assertNotFound();
$this->actingAs($user)->json($method, $this->link($server2, '/backups/' . $backup3->uuid . $endpoint))->assertNotFound();
$this->actingAs($user)->json($method, $this->link($server3, '/backups/' . $backup3->uuid . $endpoint))->assertNotFound();
}
/**
* @return \string[][]
*/
public function methodDataProvider(): array
{
return [
['GET', ''],
['GET', '/download'],
['DELETE', ''],
];
}
}

View file

@ -69,7 +69,7 @@ class CommandControllerTest extends ClientApiIntegrationTestCase
$this->repository->expects('setServer')->with(Mockery::on(function ($value) use ($server) {
return $value->uuid === $server->uuid;
}))->andReturnSelf();
$this->repository->expects('send')->with('say Test')->andReturn(new GuzzleResponse);
$this->repository->expects('send')->with('say Test')->andReturn(new GuzzleResponse());
$response = $this->actingAs($user)->postJson("/api/client/servers/{$server->uuid}/command", [
'command' => 'say Test',

View file

@ -0,0 +1,76 @@
<?php
namespace Pterodactyl\Tests\Integration\Api\Client\Server\Database;
use Mockery;
use Pterodactyl\Models\Subuser;
use Pterodactyl\Models\Database;
use Pterodactyl\Models\DatabaseHost;
use Pterodactyl\Contracts\Extensions\HashidsInterface;
use Pterodactyl\Services\Databases\DatabasePasswordService;
use Pterodactyl\Services\Databases\DatabaseManagementService;
use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
class DatabaseAuthorizationTest extends ClientApiIntegrationTestCase
{
/**
* @dataProvider methodDataProvider
*/
public function testAccessToAServersDatabasesIsRestrictedProperly(string $method, string $endpoint)
{
// The API $user is the owner of $server1.
[$user, $server1] = $this->generateTestAccount();
// Will be a subuser of $server2.
$server2 = $this->createServerModel();
// And as no access to $server3.
$server3 = $this->createServerModel();
$host = DatabaseHost::factory()->create([]);
// Set the API $user as a subuser of server 2, but with no permissions
// to do anything with the databases for that server.
Subuser::factory()->create(['server_id' => $server2->id, 'user_id' => $user->id]);
$database1 = Database::factory()->create(['server_id' => $server1->id, 'database_host_id' => $host->id]);
$database2 = Database::factory()->create(['server_id' => $server2->id, 'database_host_id' => $host->id]);
$database3 = Database::factory()->create(['server_id' => $server3->id, 'database_host_id' => $host->id]);
$this->instance(DatabasePasswordService::class, $mock = Mockery::mock(DatabasePasswordService::class));
$this->instance(DatabaseManagementService::class, $mock2 = Mockery::mock(DatabaseManagementService::class));
if ($method === 'POST') {
$mock->expects('handle')->andReturnUndefined();
} else {
$mock2->expects('delete')->andReturnUndefined();
}
$hashids = $this->app->make(HashidsInterface::class);
// This is the only valid call for this test, accessing the database for the same
// server that the API user is the owner of.
$this->actingAs($user)->json($method, $this->link($server1, '/databases/' . $hashids->encode($database1->id) . $endpoint))
->assertStatus($method === 'DELETE' ? 204 : 200);
// This request fails because the database is valid for that server but the user
// making the request is not authorized to perform that action.
$this->actingAs($user)->json($method, $this->link($server2, '/databases/' . $hashids->encode($database2->id) . $endpoint))->assertForbidden();
// Both of these should report a 404 error due to the database being linked to
// servers that are not the same as the server in the request, or are assigned
// to a server for which the user making the request has no access to.
$this->actingAs($user)->json($method, $this->link($server1, '/databases/' . $hashids->encode($database2->id) . $endpoint))->assertNotFound();
$this->actingAs($user)->json($method, $this->link($server1, '/databases/' . $hashids->encode($database3->id) . $endpoint))->assertNotFound();
$this->actingAs($user)->json($method, $this->link($server2, '/databases/' . $hashids->encode($database3->id) . $endpoint))->assertNotFound();
$this->actingAs($user)->json($method, $this->link($server3, '/databases/' . $hashids->encode($database3->id) . $endpoint))->assertNotFound();
}
/**
* @return \string[][]
*/
public function methodDataProvider(): array
{
return [
['POST', '/rotate-password'],
['DELETE', ''],
];
}
}

View file

@ -32,7 +32,7 @@ class NetworkAllocationControllerTest extends ClientApiIntegrationTestCase
public function testServerAllocationsAreNotReturnedWithoutPermission()
{
[$user, $server] = $this->generateTestAccount();
$user2 = factory(User::class)->create();
$user2 = User::factory()->create();
$server->owner_id = $user2->id;
$server->save();
@ -49,7 +49,6 @@ class NetworkAllocationControllerTest extends ClientApiIntegrationTestCase
/**
* Tests that notes on an allocation can be set correctly.
*
* @param array $permissions
* @dataProvider updatePermissionsDataProvider
*/
public function testAllocationNotesCanBeUpdated(array $permissions)
@ -85,7 +84,7 @@ class NetworkAllocationControllerTest extends ClientApiIntegrationTestCase
public function testAllocationNotesCannotBeUpdatedByInvalidUsers()
{
[$user, $server] = $this->generateTestAccount();
$user2 = factory(User::class)->create();
$user2 = User::factory()->create();
$server->owner_id = $user2->id;
$server->save();
@ -98,14 +97,13 @@ class NetworkAllocationControllerTest extends ClientApiIntegrationTestCase
}
/**
* @param array $permissions
* @dataProvider updatePermissionsDataProvider
*/
public function testPrimaryAllocationCanBeModified(array $permissions)
{
[$user, $server] = $this->generateTestAccount($permissions);
$allocation = $server->allocation;
$allocation2 = factory(Allocation::class)->create(['node_id' => $server->node_id, 'server_id' => $server->id]);
$allocation2 = Allocation::factory()->create(['node_id' => $server->node_id, 'server_id' => $server->id]);
$server->allocation_id = $allocation->id;
$server->save();
@ -121,7 +119,7 @@ class NetworkAllocationControllerTest extends ClientApiIntegrationTestCase
public function testPrimaryAllocationCannotBeModifiedByInvalidUser()
{
[$user, $server] = $this->generateTestAccount();
$user2 = factory(User::class)->create();
$user2 = User::factory()->create();
$server->owner_id = $user2->id;
$server->save();

View file

@ -15,7 +15,6 @@ class PowerControllerTest extends ClientApiIntegrationTestCase
* an error in response. This checks against the specific permission needed to send
* the command to the server.
*
* @param string $action
* @param string[] $permissions
* @dataProvider invalidPermissionDataProvider
*/
@ -47,8 +46,6 @@ class PowerControllerTest extends ClientApiIntegrationTestCase
/**
* Test that sending a valid power actions works.
*
* @param string $action
* @param string $permission
* @dataProvider validPowerActionDataProvider
*/
public function testActionCanBeSentToServer(string $action, string $permission)
@ -74,8 +71,6 @@ class PowerControllerTest extends ClientApiIntegrationTestCase
/**
* Returns invalid permission combinations for a given power action.
*
* @return array
*/
public function invalidPermissionDataProvider(): array
{
@ -88,9 +83,6 @@ class PowerControllerTest extends ClientApiIntegrationTestCase
];
}
/**
* @return array
*/
public function validPowerActionDataProvider(): array
{
return [

View file

@ -25,6 +25,7 @@ class CreateServerScheduleTest extends ClientApiIntegrationTestCase
'minute' => '0',
'hour' => '*/2',
'day_of_week' => '2',
'month' => '1',
'day_of_month' => '*',
]);
@ -39,6 +40,7 @@ class CreateServerScheduleTest extends ClientApiIntegrationTestCase
$this->assertSame('0', $schedule->cron_minute);
$this->assertSame('*/2', $schedule->cron_hour);
$this->assertSame('2', $schedule->cron_day_of_week);
$this->assertSame('1', $schedule->cron_month);
$this->assertSame('*', $schedule->cron_day_of_month);
$this->assertSame('Test Schedule', $schedule->name);
@ -69,6 +71,7 @@ class CreateServerScheduleTest extends ClientApiIntegrationTestCase
'minute' => '*',
'hour' => '*',
'day_of_month' => '*',
'month' => '*',
'day_of_week' => '*',
])
->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY)
@ -87,9 +90,6 @@ class CreateServerScheduleTest extends ClientApiIntegrationTestCase
->assertForbidden();
}
/**
* @return array
*/
public function permissionsDataProvider(): array
{
return [[[]], [[Permission::ACTION_SCHEDULE_CREATE]]];

View file

@ -20,8 +20,8 @@ class DeleteServerScheduleTest extends ClientApiIntegrationTestCase
{
[$user, $server] = $this->generateTestAccount($permissions);
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$task = factory(Task::class)->create(['schedule_id' => $schedule->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
$task = Task::factory()->create(['schedule_id' => $schedule->id]);
$this->actingAs($user)
->deleteJson("/api/client/servers/{$server->uuid}/schedules/{$schedule->id}")
@ -52,7 +52,7 @@ class DeleteServerScheduleTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount();
[, $server2] = $this->generateTestAccount(['user_id' => $user->id]);
$schedule = factory(Schedule::class)->create(['server_id' => $server2->id]);
$schedule = Schedule::factory()->create(['server_id' => $server2->id]);
$this->actingAs($user)
->deleteJson("/api/client/servers/{$server->uuid}/schedules/{$schedule->id}")
@ -69,7 +69,7 @@ class DeleteServerScheduleTest extends ClientApiIntegrationTestCase
{
[$user, $server] = $this->generateTestAccount([Permission::ACTION_SCHEDULE_UPDATE]);
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
$this->actingAs($user)
->deleteJson("/api/client/servers/{$server->uuid}/schedules/{$schedule->id}")
@ -78,9 +78,6 @@ class DeleteServerScheduleTest extends ClientApiIntegrationTestCase
$this->assertDatabaseHas('schedules', ['id' => $schedule->id]);
}
/**
* @return array
*/
public function permissionsDataProvider(): array
{
return [[[]], [[Permission::ACTION_SCHEDULE_DELETE]]];

View file

@ -15,7 +15,6 @@ class ExecuteScheduleTest extends ClientApiIntegrationTestCase
/**
* Test that a schedule can be executed and is updated in the database correctly.
*
* @param array $permissions
* @dataProvider permissionsDataProvider
*/
public function testScheduleIsExecutedRightAway(array $permissions)
@ -25,7 +24,7 @@ class ExecuteScheduleTest extends ClientApiIntegrationTestCase
Bus::fake();
/** @var \Pterodactyl\Models\Schedule $schedule */
$schedule = factory(Schedule::class)->create([
$schedule = Schedule::factory()->create([
'server_id' => $server->id,
]);
@ -35,7 +34,7 @@ class ExecuteScheduleTest extends ClientApiIntegrationTestCase
$response->assertJsonPath('errors.0.detail', 'Cannot process schedule for task execution: no tasks are registered.');
/** @var \Pterodactyl\Models\Task $task */
$task = factory(Task::class)->create([
$task = Task::factory()->create([
'schedule_id' => $schedule->id,
'sequence_id' => 1,
'time_offset' => 2,
@ -60,12 +59,12 @@ class ExecuteScheduleTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount();
/** @var \Pterodactyl\Models\Schedule $schedule */
$schedule = factory(Schedule::class)->create([
$schedule = Schedule::factory()->create([
'server_id' => $server->id,
'is_active' => false,
]);
$response = $this->actingAs($user)->postJson($this->link($schedule, "/execute"));
$response = $this->actingAs($user)->postJson($this->link($schedule, '/execute'));
$response->assertStatus(Response::HTTP_BAD_REQUEST);
$response->assertJsonPath('errors.0.code', 'BadRequestHttpException');
@ -80,14 +79,11 @@ class ExecuteScheduleTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount([Permission::ACTION_SCHEDULE_CREATE]);
/** @var \Pterodactyl\Models\Schedule $schedule */
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
$this->actingAs($user)->postJson($this->link($schedule, '/execute'))->assertForbidden();
}
/**
* @return array
*/
public function permissionsDataProvider(): array
{
return [[[]], [[Permission::ACTION_SCHEDULE_UPDATE]]];

View file

@ -24,7 +24,7 @@ class GetServerSchedulesTest extends ClientApiIntegrationTestCase
* Test that schedules for a server are returned.
*
* @param array $permissions
* @param bool $individual
* @param bool $individual
* @dataProvider permissionsDataProvider
*/
public function testServerSchedulesAreReturned($permissions, $individual)
@ -32,9 +32,9 @@ class GetServerSchedulesTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount($permissions);
/** @var \Pterodactyl\Models\Schedule $schedule */
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
/** @var \Pterodactyl\Models\Task $task */
$task = factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 1, 'time_offset' => 0]);
$task = Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 1, 'time_offset' => 0]);
$response = $this->actingAs($user)
->getJson(
@ -45,7 +45,7 @@ class GetServerSchedulesTest extends ClientApiIntegrationTestCase
->assertOk();
$prefix = $individual ? '' : 'data.0.';
if (! $individual) {
if (!$individual) {
$response->assertJsonCount(1, 'data');
}
@ -66,7 +66,7 @@ class GetServerSchedulesTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount();
[, $server2] = $this->generateTestAccount(['user_id' => $user->id]);
$schedule = factory(Schedule::class)->create(['server_id' => $server2->id]);
$schedule = Schedule::factory()->create(['server_id' => $server2->id]);
$this->actingAs($user)
->getJson("/api/client/servers/{$server->uuid}/schedules/{$schedule->id}")
@ -84,16 +84,13 @@ class GetServerSchedulesTest extends ClientApiIntegrationTestCase
->getJson("/api/client/servers/{$server->uuid}/schedules")
->assertForbidden();
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
$this->actingAs($user)
->getJson("/api/client/servers/{$server->uuid}/schedules/{$schedule->id}")
->assertForbidden();
}
/**
* @return array
*/
public function permissionsDataProvider(): array
{
return [

View file

@ -0,0 +1,70 @@
<?php
namespace Pterodactyl\Tests\Integration\Api\Client\Server\Schedule;
use Pterodactyl\Models\Subuser;
use Pterodactyl\Models\Schedule;
use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
class ScheduleAuthorizationTest extends ClientApiIntegrationTestCase
{
/**
* Tests that a subuser with access to two servers cannot improperly access a resource
* on Server A when providing a URL that points to Server B. This prevents a regression
* in the code where controllers didn't properly validate that a resource was assigned
* to the server that was also present in the URL.
*
* The comments within the test code itself are better at explaining exactly what is
* being tested and protected against.
*
* @dataProvider methodDataProvider
*/
public function testAccessToAServersSchedulesIsRestrictedProperly(string $method, string $endpoint)
{
// The API $user is the owner of $server1.
[$user, $server1] = $this->generateTestAccount();
// Will be a subuser of $server2.
$server2 = $this->createServerModel();
// And as no access to $server3.
$server3 = $this->createServerModel();
// Set the API $user as a subuser of server 2, but with no permissions
// to do anything with the schedules for that server.
Subuser::factory()->create(['server_id' => $server2->id, 'user_id' => $user->id]);
$schedule1 = Schedule::factory()->create(['server_id' => $server1->id]);
$schedule2 = Schedule::factory()->create(['server_id' => $server2->id]);
$schedule3 = Schedule::factory()->create(['server_id' => $server3->id]);
// This is the only valid call for this test, accessing the schedule for the same
// server that the API user is the owner of.
$response = $this->actingAs($user)->json($method, $this->link($server1, '/schedules/' . $schedule1->id . $endpoint));
$this->assertTrue($response->status() <= 204 || $response->status() === 400 || $response->status() === 422);
// This request fails because the schedule is valid for that server but the user
// making the request is not authorized to perform that action.
$this->actingAs($user)->json($method, $this->link($server2, '/schedules/' . $schedule2->id . $endpoint))->assertForbidden();
// Both of these should report a 404 error due to the schedules being linked to
// servers that are not the same as the server in the request, or are assigned
// to a server for which the user making the request has no access to.
$this->actingAs($user)->json($method, $this->link($server1, '/schedules/' . $schedule2->id . $endpoint))->assertNotFound();
$this->actingAs($user)->json($method, $this->link($server1, '/schedules/' . $schedule3->id . $endpoint))->assertNotFound();
$this->actingAs($user)->json($method, $this->link($server2, '/schedules/' . $schedule3->id . $endpoint))->assertNotFound();
$this->actingAs($user)->json($method, $this->link($server3, '/schedules/' . $schedule3->id . $endpoint))->assertNotFound();
}
/**
* @return \string[][]
*/
public function methodDataProvider(): array
{
return [
['GET', ''],
['POST', ''],
['DELETE', ''],
['POST', '/execute'],
['POST', '/tasks'],
];
}
}

View file

@ -19,6 +19,7 @@ class UpdateServerScheduleTest extends ClientApiIntegrationTestCase
'minute' => '5',
'hour' => '*',
'day_of_week' => '*',
'month' => '*',
'day_of_month' => '*',
'is_active' => false,
];
@ -34,8 +35,8 @@ class UpdateServerScheduleTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount($permissions);
/** @var \Pterodactyl\Models\Schedule $schedule */
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$expected = Utilities::getScheduleNextRunDate('5', '*', '*', '*');
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
$expected = Utilities::getScheduleNextRunDate('5', '*', '*', '*', '*');
$response = $this->actingAs($user)
->postJson("/api/client/servers/{$server->uuid}/schedules/{$schedule->id}", $this->updateData);
@ -59,7 +60,7 @@ class UpdateServerScheduleTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount();
[, $server2] = $this->generateTestAccount(['user_id' => $user->id]);
$schedule = factory(Schedule::class)->create(['server_id' => $server2->id]);
$schedule = Schedule::factory()->create(['server_id' => $server2->id]);
$this->actingAs($user)
->postJson("/api/client/servers/{$server->uuid}/schedules/{$schedule->id}")
@ -74,7 +75,7 @@ class UpdateServerScheduleTest extends ClientApiIntegrationTestCase
{
[$user, $server] = $this->generateTestAccount([Permission::ACTION_SCHEDULE_CREATE]);
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
$this->actingAs($user)
->postJson("/api/client/servers/{$server->uuid}/schedules/{$schedule->id}")
@ -92,7 +93,7 @@ class UpdateServerScheduleTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount();
/** @var \Pterodactyl\Models\Schedule $schedule */
$schedule = factory(Schedule::class)->create([
$schedule = Schedule::factory()->create([
'server_id' => $server->id,
'is_active' => true,
'is_processing' => true,
@ -111,9 +112,6 @@ class UpdateServerScheduleTest extends ClientApiIntegrationTestCase
$this->assertFalse($schedule->is_processing);
}
/**
* @return array
*/
public function permissionsDataProvider(): array
{
return [[[]], [[Permission::ACTION_SCHEDULE_UPDATE]]];

View file

@ -21,7 +21,7 @@ class CreateServerScheduleTaskTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount($permissions);
/** @var \Pterodactyl\Models\Schedule $schedule */
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
$this->assertEmpty($schedule->tasks);
$response = $this->actingAs($user)->postJson($this->link($schedule, '/tasks'), [
@ -51,7 +51,7 @@ class CreateServerScheduleTaskTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount();
/** @var \Pterodactyl\Models\Schedule $schedule */
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
$response = $this->actingAs($user)->postJson($this->link($schedule, '/tasks'))->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY);
@ -96,7 +96,7 @@ class CreateServerScheduleTaskTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount();
/** @var \Pterodactyl\Models\Schedule $schedule */
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
$this->actingAs($user)->postJson($this->link($schedule, '/tasks'), [
'action' => 'backup',
@ -121,8 +121,8 @@ class CreateServerScheduleTaskTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount();
/** @var \Pterodactyl\Models\Schedule $schedule */
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
factory(Task::class)->times(2)->create(['schedule_id' => $schedule->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
Task::factory()->times(2)->create(['schedule_id' => $schedule->id]);
$this->actingAs($user)->postJson($this->link($schedule, '/tasks'), [
'action' => 'command',
@ -144,7 +144,7 @@ class CreateServerScheduleTaskTest extends ClientApiIntegrationTestCase
[, $server2] = $this->generateTestAccount(['user_id' => $user->id]);
/** @var \Pterodactyl\Models\Schedule $schedule */
$schedule = factory(Schedule::class)->create(['server_id' => $server2->id]);
$schedule = Schedule::factory()->create(['server_id' => $server2->id]);
$this->actingAs($user)
->postJson("/api/client/servers/{$server->uuid}/schedules/{$schedule->id}/tasks")
@ -160,16 +160,13 @@ class CreateServerScheduleTaskTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount([Permission::ACTION_SCHEDULE_CREATE]);
/** @var \Pterodactyl\Models\Schedule $schedule */
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
$this->actingAs($user)
->postJson($this->link($schedule, '/tasks'))
->assertForbidden();
}
/**
* @return array
*/
public function permissionsDataProvider(): array
{
return [[[]], [[Permission::ACTION_SCHEDULE_UPDATE]]];

View file

@ -19,8 +19,8 @@ class DeleteScheduleTaskTest extends ClientApiIntegrationTestCase
$server2 = $this->createServerModel();
[$user] = $this->generateTestAccount();
$schedule = factory(Schedule::class)->create(['server_id' => $server2->id]);
$task = factory(Task::class)->create(['schedule_id' => $schedule->id]);
$schedule = Schedule::factory()->create(['server_id' => $server2->id]);
$task = Task::factory()->create(['schedule_id' => $schedule->id]);
$this->actingAs($user)->deleteJson($this->link($task))->assertNotFound();
}
@ -33,9 +33,9 @@ class DeleteScheduleTaskTest extends ClientApiIntegrationTestCase
{
[$user, $server] = $this->generateTestAccount();
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$schedule2 = factory(Schedule::class)->create(['server_id' => $server->id]);
$task = factory(Task::class)->create(['schedule_id' => $schedule->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
$schedule2 = Schedule::factory()->create(['server_id' => $server->id]);
$task = Task::factory()->create(['schedule_id' => $schedule->id]);
$this->actingAs($user)->deleteJson("/api/client/servers/{$server->uuid}/schedules/{$schedule2->id}/tasks/{$task->id}")->assertNotFound();
}
@ -47,12 +47,12 @@ class DeleteScheduleTaskTest extends ClientApiIntegrationTestCase
{
[$user, $server] = $this->generateTestAccount([Permission::ACTION_SCHEDULE_CREATE]);
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$task = factory(Task::class)->create(['schedule_id' => $schedule->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
$task = Task::factory()->create(['schedule_id' => $schedule->id]);
$this->actingAs($user)->deleteJson($this->link($task))->assertForbidden();
$user2 = factory(User::class)->create();
$user2 = User::factory()->create();
$this->actingAs($user2)->deleteJson($this->link($task))->assertNotFound();
}
@ -65,12 +65,12 @@ class DeleteScheduleTaskTest extends ClientApiIntegrationTestCase
{
[$user, $server] = $this->generateTestAccount();
$schedule = factory(Schedule::class)->create(['server_id' => $server->id]);
$schedule = Schedule::factory()->create(['server_id' => $server->id]);
$tasks = [
factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 1]),
factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 2]),
factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 3]),
factory(Task::class)->create(['schedule_id' => $schedule->id, 'sequence_id' => 4]),
Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 1]),
Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 2]),
Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 3]),
Task::factory()->create(['schedule_id' => $schedule->id, 'sequence_id' => 4]),
];
$response = $this->actingAs($user)->deleteJson($this->link($tasks[1]));

View file

@ -110,17 +110,11 @@ class SettingsControllerTest extends ClientApiIntegrationTestCase
$this->assertTrue($server->isInstalled());
}
/**
* @return array
*/
public function renamePermissionsDataProvider(): array
{
return [[[]], [[Permission::ACTION_SETTINGS_RENAME]]];
}
/**
* @return array
*/
public function reinstallPermissionsDataProvider(): array
{
return [[[]], [[Permission::ACTION_SETTINGS_REINSTALL]]];

View file

@ -34,7 +34,7 @@ class GetStartupAndVariablesTest extends ClientApiIntegrationTestCase
])->save();
$server = $server->refresh();
$response = $this->actingAs($user)->getJson($this->link($server) . "/startup");
$response = $this->actingAs($user)->getJson($this->link($server) . '/startup');
$response->assertOk();
$response->assertJsonPath('meta.startup_command', 'java bungeecord.jar --version [hidden]');
@ -53,10 +53,10 @@ class GetStartupAndVariablesTest extends ClientApiIntegrationTestCase
public function testStartupDataIsNotReturnedWithoutPermission()
{
[$user, $server] = $this->generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT]);
$this->actingAs($user)->getJson($this->link($server) . "/startup")->assertForbidden();
$this->actingAs($user)->getJson($this->link($server) . '/startup')->assertForbidden();
$user2 = factory(User::class)->create();
$this->actingAs($user2)->getJson($this->link($server) . "/startup")->assertNotFound();
$user2 = User::factory()->create();
$this->actingAs($user2)->getJson($this->link($server) . '/startup')->assertNotFound();
}
/**

View file

@ -49,7 +49,6 @@ class UpdateStartupVariableTest extends ClientApiIntegrationTestCase
* Test that variables that are either not user_viewable, or not user_editable, cannot be
* updated via this endpoint.
*
* @param array $permissions
* @dataProvider permissionsDataProvider
*/
public function testStartupVariableCannotBeUpdatedIfNotUserViewableOrEditable(array $permissions)
@ -145,10 +144,10 @@ class UpdateStartupVariableTest extends ClientApiIntegrationTestCase
public function testStartupVariableCannotBeUpdatedIfNotUserViewable()
{
[$user, $server] = $this->generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT]);
$this->actingAs($user)->putJson($this->link($server) . "/startup/variable")->assertForbidden();
$this->actingAs($user)->putJson($this->link($server) . '/startup/variable')->assertForbidden();
$user2 = factory(User::class)->create();
$this->actingAs($user2)->putJson($this->link($server) . "/startup/variable")->assertNotFound();
$user2 = User::factory()->create();
$this->actingAs($user2)->putJson($this->link($server) . '/startup/variable')->assertNotFound();
}
/**

View file

@ -24,7 +24,7 @@ class CreateServerSubuserTest extends ClientApiIntegrationTestCase
{
[$user, $server] = $this->generateTestAccount($permissions);
$response = $this->actingAs($user)->postJson($this->link($server) . "/users", [
$response = $this->actingAs($user)->postJson($this->link($server) . '/users', [
'email' => $email = $this->faker->email,
'permissions' => [
Permission::ACTION_USER_CREATE,
@ -61,7 +61,7 @@ class CreateServerSubuserTest extends ClientApiIntegrationTestCase
Permission::ACTION_CONTROL_CONSOLE,
]);
$response = $this->actingAs($user)->postJson($this->link($server) . "/users", [
$response = $this->actingAs($user)->postJson($this->link($server) . '/users', [
'email' => $email = $this->faker->email,
'permissions' => [
Permission::ACTION_USER_CREATE,
@ -83,7 +83,7 @@ class CreateServerSubuserTest extends ClientApiIntegrationTestCase
$email = str_repeat(Str::random(20), 9) . '1@gmail.com'; // 191 is the hard limit for the column in MySQL.
$response = $this->actingAs($user)->postJson($this->link($server) . "/users", [
$response = $this->actingAs($user)->postJson($this->link($server) . '/users', [
'email' => $email,
'permissions' => [
Permission::ACTION_USER_CREATE,
@ -92,7 +92,7 @@ class CreateServerSubuserTest extends ClientApiIntegrationTestCase
$response->assertOk();
$response = $this->actingAs($user)->postJson($this->link($server) . "/users", [
$response = $this->actingAs($user)->postJson($this->link($server) . '/users', [
'email' => $email . '.au',
'permissions' => [
Permission::ACTION_USER_CREATE,
@ -113,9 +113,9 @@ class CreateServerSubuserTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount();
/** @var \Pterodactyl\Models\User $existing */
$existing = factory(User::class)->create(['email' => $this->faker->email]);
$existing = User::factory()->create(['email' => $this->faker->email]);
$response = $this->actingAs($user)->postJson($this->link($server) . "/users", [
$response = $this->actingAs($user)->postJson($this->link($server) . '/users', [
'email' => $existing->email,
'permissions' => [
Permission::ACTION_USER_CREATE,
@ -135,7 +135,7 @@ class CreateServerSubuserTest extends ClientApiIntegrationTestCase
{
[$user, $server] = $this->generateTestAccount();
$response = $this->actingAs($user)->postJson($this->link($server) . "/users", [
$response = $this->actingAs($user)->postJson($this->link($server) . '/users', [
'email' => $email = $this->faker->email,
'permissions' => [
Permission::ACTION_USER_CREATE,
@ -144,7 +144,7 @@ class CreateServerSubuserTest extends ClientApiIntegrationTestCase
$response->assertOk();
$response = $this->actingAs($user)->postJson($this->link($server) . "/users", [
$response = $this->actingAs($user)->postJson($this->link($server) . '/users', [
'email' => $email,
'permissions' => [
Permission::ACTION_USER_CREATE,
@ -156,9 +156,6 @@ class CreateServerSubuserTest extends ClientApiIntegrationTestCase
$response->assertJsonPath('errors.0.detail', 'A user with that email address is already assigned as a subuser for this server.');
}
/**
* @return array
*/
public function permissionsDataProvider(): array
{
return [[[]], [[Permission::ACTION_USER_CREATE]]];

View file

@ -30,13 +30,13 @@ class DeleteSubuserTest extends ClientApiIntegrationTestCase
[$user, $server] = $this->generateTestAccount();
/** @var \Pterodactyl\Models\User $differentUser */
$differentUser = factory(User::class)->create();
$differentUser = User::factory()->create();
// Generate a UUID that lines up with a user in the database if it were to be cast to an int.
$uuid = $differentUser->id . str_repeat('a', strlen((string)$differentUser->id)) . substr(Uuid::uuid4()->toString(), 8);
$uuid = $differentUser->id . str_repeat('a', strlen((string) $differentUser->id)) . substr(Uuid::uuid4()->toString(), 8);
/** @var \Pterodactyl\Models\User $subuser */
$subuser = factory(User::class)->create(['uuid' => $uuid]);
$subuser = User::factory()->create(['uuid' => $uuid]);
Subuser::query()->forceCreate([
'user_id' => $subuser->id,
@ -52,7 +52,7 @@ class DeleteSubuserTest extends ClientApiIntegrationTestCase
// anything in the database.
$uuid = '18180000' . substr(Uuid::uuid4()->toString(), 8);
/** @var \Pterodactyl\Models\User $subuser */
$subuser = factory(User::class)->create(['uuid' => $uuid]);
$subuser = User::factory()->create(['uuid' => $uuid]);
Subuser::query()->forceCreate([
'user_id' => $subuser->id,

View file

@ -0,0 +1,60 @@
<?php
namespace Pterodactyl\Tests\Integration\Api\Client\Server\Subuser;
use Mockery;
use Pterodactyl\Models\User;
use Pterodactyl\Models\Subuser;
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
class SubuserAuthorizationTest extends ClientApiIntegrationTestCase
{
/**
* Test that mismatched subusers are not accessible to a server.
*
* @dataProvider methodDataProvider
*/
public function testUserCannotAccessResourceBelongingToOtherServers(string $method)
{
// Generic subuser, the specific resource we're trying to access.
/** @var \Pterodactyl\Models\User $internal */
$internal = User::factory()->create();
// The API $user is the owner of $server1.
[$user, $server1] = $this->generateTestAccount();
// Will be a subuser of $server2.
$server2 = $this->createServerModel();
// And as no access to $server3.
$server3 = $this->createServerModel();
// Set the API $user as a subuser of server 2, but with no permissions
// to do anything with the subusers for that server.
Subuser::factory()->create(['server_id' => $server2->id, 'user_id' => $user->id]);
Subuser::factory()->create(['server_id' => $server1->id, 'user_id' => $internal->id]);
Subuser::factory()->create(['server_id' => $server2->id, 'user_id' => $internal->id]);
Subuser::factory()->create(['server_id' => $server3->id, 'user_id' => $internal->id]);
$this->instance(DaemonServerRepository::class, $mock = Mockery::mock(DaemonServerRepository::class));
if ($method === 'DELETE') {
$mock->expects('setServer->revokeUserJTI')->with($internal->id)->andReturnUndefined();
}
// This route is acceptable since they're accessing a subuser on their own server.
$this->actingAs($user)->json($method, $this->link($server1, '/users/' . $internal->uuid))->assertStatus($method === 'POST' ? 422 : ($method === 'DELETE' ? 204 : 200));
// This route can be revealed since the subuser belongs to the correct server, but
// errors out with a 403 since $user does not have the right permissions for this.
$this->actingAs($user)->json($method, $this->link($server2, '/users/' . $internal->uuid))->assertForbidden();
$this->actingAs($user)->json($method, $this->link($server3, '/users/' . $internal->uuid))->assertNotFound();
}
/**
* @return \string[][]
*/
public function methodDataProvider(): array
{
return [['GET'], ['POST'], ['DELETE']];
}
}

View file

@ -2,12 +2,13 @@
namespace Pterodactyl\Tests\Integration\Api\Client\Server;
use Carbon\Carbon;
use Lcobucci\JWT\Parser;
use Carbon\CarbonImmutable;
use Illuminate\Http\Response;
use Lcobucci\JWT\Configuration;
use Pterodactyl\Models\Permission;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
class WebsocketControllerTest extends ClientApiIntegrationTestCase
@ -32,8 +33,6 @@ class WebsocketControllerTest extends ClientApiIntegrationTestCase
*/
public function testJwtAndWebsocketUrlAreReturnedForServerOwner()
{
CarbonImmutable::setTestNow(Carbon::now());
/** @var \Pterodactyl\Models\User $user */
/** @var \Pterodactyl\Models\Server $server */
[$user, $server] = $this->generateTestAccount();
@ -51,22 +50,33 @@ class WebsocketControllerTest extends ClientApiIntegrationTestCase
$this->assertStringStartsWith('wss://', $connection, 'Failed asserting that websocket connection address has expected "wss://" prefix.');
$this->assertStringEndsWith("/api/servers/{$server->uuid}/ws", $connection, 'Failed asserting that websocket connection address uses expected Wings endpoint.');
$token = (new Parser)->parse($response->json('data.token'));
$config = Configuration::forSymmetricSigner(new Sha256(), $key = InMemory::plainText($server->node->getDecryptedKey()));
$config->setValidationConstraints(new SignedWith(new Sha256(), $key));
/** @var \Lcobucci\JWT\Token\Plain $token */
$token = $config->parser()->parse($response->json('data.token'));
$this->assertTrue(
$token->verify(new Sha256, $server->node->getDecryptedKey()),
$config->validator()->validate($token, ...$config->validationConstraints()),
'Failed to validate that the JWT data returned was signed using the Node\'s secret key.'
);
// The way we generate times for the JWT will truncate the microseconds from the
// time, but CarbonImmutable::now() will include them, thus causing test failures.
//
// This little chunk of logic just strips those out by generating a new CarbonImmutable
// instance from the current timestamp, which is how the JWT works. We also need to
// switch to UTC here for consistency.
$expect = CarbonImmutable::createFromTimestamp(CarbonImmutable::now()->getTimestamp())->timezone('UTC');
// Check that the claims are generated correctly.
$this->assertSame(config('app.url'), $token->getClaim('iss'));
$this->assertSame($server->node->getConnectionAddress(), $token->getClaim('aud'));
$this->assertSame(CarbonImmutable::now()->getTimestamp(), $token->getClaim('iat'));
$this->assertSame(CarbonImmutable::now()->subMinutes(5)->getTimestamp(), $token->getClaim('nbf'));
$this->assertSame(CarbonImmutable::now()->addMinutes(10)->getTimestamp(), $token->getClaim('exp'));
$this->assertSame($user->id, $token->getClaim('user_id'));
$this->assertSame($server->uuid, $token->getClaim('server_uuid'));
$this->assertSame(['*'], $token->getClaim('permissions'));
$this->assertTrue($token->hasBeenIssuedBy(config('app.url')));
$this->assertTrue($token->isPermittedFor($server->node->getConnectionAddress()));
$this->assertEquals($expect, $token->claims()->get('iat'));
$this->assertEquals($expect->subMinutes(5), $token->claims()->get('nbf'));
$this->assertEquals($expect->addMinutes(10), $token->claims()->get('exp'));
$this->assertSame($user->id, $token->claims()->get('user_id'));
$this->assertSame($server->uuid, $token->claims()->get('server_uuid'));
$this->assertSame(['*'], $token->claims()->get('permissions'));
}
/**
@ -85,14 +95,17 @@ class WebsocketControllerTest extends ClientApiIntegrationTestCase
$response->assertOk();
$response->assertJsonStructure(['data' => ['token', 'socket']]);
$token = (new Parser)->parse($response->json('data.token'));
$config = Configuration::forSymmetricSigner(new Sha256(), $key = InMemory::plainText($server->node->getDecryptedKey()));
$config->setValidationConstraints(new SignedWith(new Sha256(), $key));
/** @var \Lcobucci\JWT\Token\Plain $token */
$token = $config->parser()->parse($response->json('data.token'));
$this->assertTrue(
$token->verify(new Sha256, $server->node->getDecryptedKey()),
$config->validator()->validate($token, ...$config->validationConstraints()),
'Failed to validate that the JWT data returned was signed using the Node\'s secret key.'
);
// Check that the claims are generated correctly.
$this->assertSame($permissions, $token->getClaim('permissions'));
$this->assertSame($permissions, $token->claims()->get('permissions'));
}
}