Merge pull request #7251 from pixelfed/fix/delete-account-purge-status-edits

Purge status_edits on account and status deletion
pull/7256/head
Shlee 1 week ago committed by GitHub
commit a510682122
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -33,6 +33,7 @@ use App\Models\RemoteReport;
use App\Models\Report;
use App\Models\Status;
use App\Models\StatusArchived;
use App\Models\StatusEdit;
use App\Models\StatusHashtag;
use App\Models\StatusView;
use App\Models\Story;
@ -130,6 +131,10 @@ class DeleteAccountPipeline implements ShouldQueue
StatusView::whereProfileId($id)->delete();
// Purge edit history (prior caption/CW versions). status_edits has no
// FK/cascade and StatusEdit has no SoftDeletes, so this is a hard delete.
StatusEdit::whereProfileId($id)->delete();
ProfileAlias::whereProfileId($id)->delete();
ProfileMigration::whereProfileId($id)->delete();

@ -15,6 +15,7 @@ use App\Models\Notification;
use App\Models\Report;
use App\Models\Status;
use App\Models\StatusArchived;
use App\Models\StatusEdit;
use App\Models\StatusHashtag;
use App\Models\StatusView;
use App\Services\ActivityPubDeliveryService;
@ -175,6 +176,9 @@ class StatusDelete implements ShouldQueue
->delete();
StatusArchived::whereStatusId($status->id)->delete();
// Purge edit history so single-status deletion doesn't leave prior
// caption/CW versions behind (status_edits has no FK/cascade).
StatusEdit::whereStatusId($status->id)->delete();
// Model-based delete so StatusHashtagObserver::deleted() runs and
// decrements hashtags.cached_count (a query-builder delete bypasses it).
StatusHashtag::whereStatusId($status->id)->get()->each->delete();

@ -0,0 +1,53 @@
<?php
use App\Jobs\DeletePipeline\DeleteAccountPipeline;
use App\Models\Status;
use App\Models\StatusEdit;
use App\Models\User;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
uses(LazilyRefreshDatabase::class);
/*
|--------------------------------------------------------------------------
| DeleteAccountPipeline edit-history cleanup
|--------------------------------------------------------------------------
|
| Account deletion must purge status_edits (prior caption/CW versions the user
| edited). The table has no FK/cascade and StatusEdit has no SoftDeletes, so it
| was left behind indefinitely while the pipeline scrubbed comparable
| user-authored tables (DMs, mentions, notifications, stories, collections).
|
*/
it('removes status_edits rows when an account is deleted', function () {
$user = User::factory()->create();
$user->refresh();
$pid = $user->profile_id;
$status = Status::factory()->create([
'profile_id' => $pid,
'caption' => 'edited to redact',
'type' => 'text',
'scope' => 'public',
]);
// Prior + current caption versions, as UpdateStatusService records them.
StatusEdit::create([
'status_id' => $status->id,
'profile_id' => $pid,
'caption' => 'original sensitive text',
]);
StatusEdit::create([
'status_id' => $status->id,
'profile_id' => $pid,
'caption' => 'edited to redact',
]);
expect(StatusEdit::whereProfileId($pid)->count())->toBe(2);
(new DeleteAccountPipeline($user))->handle();
// Edit history is hard-deleted; no prior caption text is left behind.
expect(StatusEdit::whereProfileId($pid)->count())->toBe(0);
});

@ -5,6 +5,7 @@ use App\Models\DirectMessage;
use App\Models\MediaTag;
use App\Models\Notification;
use App\Models\Status;
use App\Models\StatusEdit;
use App\Models\User;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
@ -83,6 +84,36 @@ it('removes associated media tags and their notifications when a status is delet
expect(Notification::find($notification->id))->toBeNull();
});
it('removes edit history (status_edits) when a status is deleted', function () {
$user = User::factory()->create();
$user->refresh();
$status = Status::factory()->create([
'profile_id' => $user->profile_id,
'type' => 'photo',
]);
// Prior + current caption versions, the way UpdateStatusService records them.
StatusEdit::create([
'status_id' => $status->id,
'profile_id' => $user->profile_id,
'caption' => 'original sensitive text',
]);
StatusEdit::create([
'status_id' => $status->id,
'profile_id' => $user->profile_id,
'caption' => 'edited to redact',
]);
expect(StatusEdit::whereStatusId($status->id)->count())->toBe(2);
(new StatusDelete($status))->handle();
// Edit history is hard-deleted alongside the status (no orphaned prior text).
expect(StatusEdit::whereStatusId($status->id)->count())->toBe(0);
expect(Status::find($status->id))->toBeNull();
});
it('still deletes a status without dms or tags', function () {
$user = User::factory()->create();
$user->refresh();

Loading…
Cancel
Save