From d3e3b1db389be191e45a8aead054884f15251b44 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 11 Jul 2021 12:15:39 -0700 Subject: [PATCH] Test that a deleted backup makes an audit log entry --- app/Models/AuditLog.php | 1 + app/Models/Backup.php | 11 ++++ .../Client/ClientApiIntegrationTestCase.php | 8 ++- .../Client/Server/Backup/DeleteBackupTest.php | 65 +++++++++++++++++++ 4 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 tests/Integration/Api/Client/Server/Backup/DeleteBackupTest.php diff --git a/app/Models/AuditLog.php b/app/Models/AuditLog.php index eee50d694..1cd942e95 100644 --- a/app/Models/AuditLog.php +++ b/app/Models/AuditLog.php @@ -69,6 +69,7 @@ class AuditLog extends Model * @var string[] */ protected $casts = [ + 'is_system' => 'bool', 'device' => 'array', 'metadata' => 'array', ]; diff --git a/app/Models/Backup.php b/app/Models/Backup.php index 3f791abf4..26dcc7242 100644 --- a/app/Models/Backup.php +++ b/app/Models/Backup.php @@ -21,6 +21,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property \Carbon\CarbonImmutable $updated_at * @property \Carbon\CarbonImmutable|null $deleted_at * @property \Pterodactyl\Models\Server $server + * @property \Pterodactyl\Models\AuditLog[] $audits */ class Backup extends Model { @@ -98,4 +99,14 @@ class Backup extends Model { return $this->belongsTo(Server::class); } + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function audits() + { + return $this->hasMany(AuditLog::class, 'metadata->backup_uuid', 'uuid') + ->where('action', 'LIKE', 'server:backup.%'); + // ->where('metadata->backup_uuid', $this->uuid); + } } diff --git a/tests/Integration/Api/Client/ClientApiIntegrationTestCase.php b/tests/Integration/Api/Client/ClientApiIntegrationTestCase.php index cd8fadd35..8075c9fc2 100644 --- a/tests/Integration/Api/Client/ClientApiIntegrationTestCase.php +++ b/tests/Integration/Api/Client/ClientApiIntegrationTestCase.php @@ -7,6 +7,7 @@ use Pterodactyl\Models\Node; use Pterodactyl\Models\Task; use Pterodactyl\Models\User; use Webmozart\Assert\Assert; +use InvalidArgumentException; use Pterodactyl\Models\Backup; use Pterodactyl\Models\Server; use Pterodactyl\Models\Subuser; @@ -60,8 +61,6 @@ abstract class ClientApiIntegrationTestCase extends IntegrationTestCase */ protected function link($model, $append = null): string { - Assert::isInstanceOfAny($model, [Server::class, Schedule::class, Task::class, Allocation::class]); - $link = ''; switch (get_class($model)) { case Server::class: @@ -76,6 +75,11 @@ abstract class ClientApiIntegrationTestCase extends IntegrationTestCase case Allocation::class: $link = "/api/client/servers/{$model->server->uuid}/network/allocations/{$model->id}"; break; + case Backup::class: + $link = "/api/client/servers/{$model->server->uuid}/backups/{$model->uuid}"; + break; + default: + throw new InvalidArgumentException(sprintf('Cannot create link for Model of type %s', class_basename($model))); } return $link . ($append ? '/' . ltrim($append, '/') : ''); diff --git a/tests/Integration/Api/Client/Server/Backup/DeleteBackupTest.php b/tests/Integration/Api/Client/Server/Backup/DeleteBackupTest.php new file mode 100644 index 000000000..0fc80610d --- /dev/null +++ b/tests/Integration/Api/Client/Server/Backup/DeleteBackupTest.php @@ -0,0 +1,65 @@ +repository = $this->mock(DaemonBackupRepository::class); + } + + public function testUserWithoutPermissionCannotDeleteBackup() + { + [$user, $server] = $this->generateTestAccount([Permission::ACTION_BACKUP_CREATE]); + + $backup = Backup::factory()->create(['server_id' => $server->id]); + + $this->actingAs($user)->deleteJson($this->link($backup)) + ->assertStatus(Response::HTTP_FORBIDDEN); + } + + /** + * Tests that a backup can be deleted for a server and that it is properly updated + * in the database. Once deleted there should also be a corresponding record in the + * audit logs table for this API call. + */ + public function testBackupCanBeDeleted() + { + [$user, $server] = $this->generateTestAccount([Permission::ACTION_BACKUP_DELETE]); + + /** @var \Pterodactyl\Models\Backup $backup */ + $backup = Backup::factory()->create(['server_id' => $server->id]); + + $this->repository->expects('setServer->delete')->with(Mockery::on(function ($value) use ($backup) { + return $value instanceof Backup && $value->uuid === $backup->uuid; + }))->andReturn(new Response()); + + $this->actingAs($user)->deleteJson($this->link($backup))->assertStatus(Response::HTTP_NO_CONTENT); + + $backup->refresh(); + + $this->assertNotNull($backup->deleted_at); + + $this->actingAs($user)->deleteJson($this->link($backup))->assertStatus(Response::HTTP_NOT_FOUND); + + $event = $backup->audits()->where('action', AuditLog::SERVER__BACKUP_DELETED)->latest()->first(); + + $this->assertNotNull($event); + $this->assertFalse($event->is_system); + $this->assertEquals($backup->server_id, $event->server_id); + $this->assertEquals($user->id, $event->user_id); + } +}