Files
Virtnet.bond/extensions/Others/ComplianceTools/Services/AccountDeletionService.php
2025-11-30 11:57:22 +01:00

144 lines
4.3 KiB
PHP

<?php
namespace Paymenter\Extensions\Others\ComplianceTools\Services;
use App\Helpers\ExtensionHelper;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Paymenter\Extensions\Others\ComplianceTools\Models\AccountDeletionRequest;
class AccountDeletionService
{
public function __construct(protected AuditLogService $auditLog)
{
}
public function submit(User $user, ?string $reason = null, array $metadata = []): AccountDeletionRequest
{
$request = AccountDeletionRequest::create([
'user_id' => $user->getKey(),
'reason' => $reason,
'status' => AccountDeletionRequest::STATUS_PENDING,
'metadata' => $metadata,
]);
$this->auditLog->record(
category: 'account_deletion',
event: 'requested',
context: ['deletion_request_id' => $request->getKey()],
subject: $user
);
$extension = ExtensionHelper::getExtension('other', 'ComplianceTools');
if ($extension && $extension->config('auto_approve_deletions')) {
$this->approve($request, null, '[auto-approved]');
}
return $request->fresh();
}
public function approve(AccountDeletionRequest $request, ?User $processor = null, ?string $notes = null): AccountDeletionRequest
{
if ($request->status === AccountDeletionRequest::STATUS_COMPLETED) {
return $request;
}
DB::transaction(function () use ($request, $processor, $notes) {
$request->forceFill([
'status' => AccountDeletionRequest::STATUS_APPROVED,
'admin_notes' => $notes,
'processed_by' => $processor?->getKey(),
'processed_at' => now(),
'snapshot' => $this->snapshotUser($request->user),
])->save();
$this->anonymizeUser($request->user);
$request->forceFill(['status' => AccountDeletionRequest::STATUS_COMPLETED])->save();
});
$this->auditLog->record(
category: 'account_deletion',
event: 'approved',
context: ['deletion_request_id' => $request->getKey()],
subject: $request->user,
actor: $processor
);
return $request;
}
public function deny(AccountDeletionRequest $request, User $processor, ?string $notes = null): AccountDeletionRequest
{
if ($request->status !== AccountDeletionRequest::STATUS_PENDING) {
return $request;
}
$request->forceFill([
'status' => AccountDeletionRequest::STATUS_DENIED,
'admin_notes' => $notes,
'processed_by' => $processor->getKey(),
'processed_at' => now(),
])->save();
$this->auditLog->record(
category: 'account_deletion',
event: 'denied',
context: ['deletion_request_id' => $request->getKey()],
subject: $request->user,
actor: $processor
);
return $request;
}
protected function snapshotUser(User $user): array
{
return [
'user' => $user->only([
'id',
'first_name',
'last_name',
'email',
'created_at',
]),
];
}
protected function anonymizeUser(User $user): void
{
$anonymizedEmail = sprintf('deleted-user-%s-%s@anonymous.invalid', $user->getKey(), Str::random(10));
$updates = [
'first_name' => null,
'last_name' => null,
'email' => $anonymizedEmail,
'password' => Hash::make(Str::random(32)),
'tfa_secret' => null,
];
if (method_exists($user, 'properties') && $user->properties()->exists()) {
$user->properties()->delete();
}
$user->forceFill($updates)->save();
if (method_exists($user, 'tokens')) {
$user->tokens()->delete();
}
if (method_exists($user, 'sessions')) {
$user->sessions()->delete();
}
$this->auditLog->record(
category: 'account_deletion',
event: 'anonymized',
context: ['user_id' => $user->getKey(), 'email' => $anonymizedEmail],
subject: $user
);
}
}