185 lines
6.4 KiB
PHP
185 lines
6.4 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\Admin;
|
|
use App\Models\Bank;
|
|
use App\Models\Organization;
|
|
use App\Models\User;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Spatie\Activitylog\Models\Activity;
|
|
|
|
class CleanupIpAddresses extends Command
|
|
{
|
|
protected $signature = 'ip:cleanup {--dry-run : Show what would be cleaned without actually deleting}';
|
|
protected $description;
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
$this->description = 'Anonymize IP addresses older than ' .
|
|
timebank_config('ip_retention.retention_days') . ' days for GDPR compliance';
|
|
}
|
|
|
|
public function handle()
|
|
{
|
|
$retentionDays = timebank_config('ip_retention.retention_days', 180);
|
|
$cutoffDate = now()->subDays($retentionDays);
|
|
$isDryRun = $this->option('dry-run');
|
|
|
|
$this->info('Starting IP address cleanup...');
|
|
$this->info('Retention period: ' . $retentionDays . ' days');
|
|
$this->info('Cutoff date: ' . $cutoffDate->toDateTimeString());
|
|
|
|
if ($isDryRun) {
|
|
$this->warn('DRY RUN MODE - No changes will be made');
|
|
}
|
|
|
|
$totalAnonymized = 0;
|
|
|
|
// Cleanup profile tables (users, organizations, banks, admins)
|
|
$profileModels = [
|
|
'users' => User::class,
|
|
'organizations' => Organization::class,
|
|
'banks' => Bank::class,
|
|
'admins' => Admin::class,
|
|
];
|
|
|
|
foreach ($profileModels as $tableName => $modelClass) {
|
|
$count = $this->cleanupProfileTable($tableName, $modelClass, $cutoffDate, $isDryRun);
|
|
$totalAnonymized += $count;
|
|
}
|
|
|
|
// Cleanup activity log IP addresses
|
|
$activityLogCount = $this->cleanupActivityLog($cutoffDate, $isDryRun);
|
|
$totalAnonymized += $activityLogCount;
|
|
|
|
$action = $isDryRun ? 'Would anonymize' : 'Anonymized';
|
|
$this->info("✓ {$action} {$totalAnonymized} IP address records in total.");
|
|
|
|
// Log the cleanup action
|
|
if (!$isDryRun) {
|
|
Log::info('IP address cleanup completed', [
|
|
'retention_days' => $retentionDays,
|
|
'cutoff_date' => $cutoffDate->toDateTimeString(),
|
|
'total_anonymized' => $totalAnonymized,
|
|
]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Cleanup IP addresses from profile tables
|
|
*
|
|
* @param string $tableName
|
|
* @param string $modelClass
|
|
* @param \Carbon\Carbon $cutoffDate
|
|
* @param bool $isDryRun
|
|
* @return int Number of records anonymized
|
|
*/
|
|
protected function cleanupProfileTable(string $tableName, string $modelClass, $cutoffDate, bool $isDryRun): int
|
|
{
|
|
$this->line("\nProcessing {$tableName} table...");
|
|
|
|
// Find profiles with last_login_ip that should be anonymized
|
|
$query = $modelClass::whereNotNull('last_login_ip')
|
|
->where('last_login_ip', '!=', '')
|
|
->where(function ($q) use ($cutoffDate) {
|
|
// Anonymize if last_login_at is older than cutoff date
|
|
$q->where('last_login_at', '<', $cutoffDate)
|
|
// Or if last_login_at is null (should not happen, but handle it)
|
|
->orWhereNull('last_login_at');
|
|
});
|
|
|
|
$count = $query->count();
|
|
|
|
if ($count === 0) {
|
|
$this->line(" No IP addresses to anonymize in {$tableName}");
|
|
return 0;
|
|
}
|
|
|
|
if ($isDryRun) {
|
|
$this->warn(" Would anonymize {$count} IP addresses in {$tableName}");
|
|
|
|
// Show some examples in dry run
|
|
$examples = $query->take(3)->get(['id', 'name', 'last_login_ip', 'last_login_at']);
|
|
if ($examples->isNotEmpty()) {
|
|
$this->line(" Examples:");
|
|
foreach ($examples as $example) {
|
|
$loginDate = 'never';
|
|
if ($example->last_login_at) {
|
|
$loginDate = is_string($example->last_login_at)
|
|
? $example->last_login_at
|
|
: $example->last_login_at->toDateString();
|
|
}
|
|
$this->line(" - ID {$example->id} ({$example->name}): {$example->last_login_ip} (last login: {$loginDate})");
|
|
}
|
|
}
|
|
} else {
|
|
// Anonymize by setting to null
|
|
$updated = $query->update(['last_login_ip' => null]);
|
|
$this->info(" ✓ Anonymized {$updated} IP addresses in {$tableName}");
|
|
}
|
|
|
|
return $count;
|
|
}
|
|
|
|
/**
|
|
* Cleanup IP addresses from activity log
|
|
*
|
|
* @param \Carbon\Carbon $cutoffDate
|
|
* @param bool $isDryRun
|
|
* @return int Number of records anonymized
|
|
*/
|
|
protected function cleanupActivityLog($cutoffDate, bool $isDryRun): int
|
|
{
|
|
$this->line("\nProcessing activity_log table...");
|
|
|
|
// Find activity logs with IP addresses older than cutoff date
|
|
$query = Activity::whereNotNull('properties->ip')
|
|
->where('created_at', '<', $cutoffDate);
|
|
|
|
$count = $query->count();
|
|
|
|
if ($count === 0) {
|
|
$this->line(" No IP addresses to anonymize in activity_log");
|
|
return 0;
|
|
}
|
|
|
|
if ($isDryRun) {
|
|
$this->warn(" Would anonymize {$count} IP addresses in activity_log");
|
|
|
|
// Show some examples in dry run
|
|
$examples = $query->take(3)->get(['id', 'log_name', 'properties', 'created_at']);
|
|
if ($examples->isNotEmpty()) {
|
|
$this->line(" Examples:");
|
|
foreach ($examples as $example) {
|
|
$ip = $example->properties['ip'] ?? 'N/A';
|
|
$this->line(" - ID {$example->id} ({$example->log_name}): {$ip} (date: {$example->created_at->toDateString()})");
|
|
}
|
|
}
|
|
} else {
|
|
// Anonymize by removing IP from properties JSON
|
|
$activities = $query->get();
|
|
$updated = 0;
|
|
|
|
foreach ($activities as $activity) {
|
|
$properties = $activity->properties;
|
|
if (isset($properties['ip'])) {
|
|
unset($properties['ip']);
|
|
$activity->properties = $properties;
|
|
$activity->save();
|
|
$updated++;
|
|
}
|
|
}
|
|
|
|
$this->info(" ✓ Anonymized {$updated} IP addresses in activity_log");
|
|
}
|
|
|
|
return $count;
|
|
}
|
|
}
|