144 lines
4.3 KiB
PHP
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
|
|
);
|
|
}
|
|
}
|