182 lines
4.9 KiB
PHP
182 lines
4.9 KiB
PHP
<?php
|
|
|
|
namespace Paymenter\Extensions\Others\Security\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use App\Models\User;
|
|
use Carbon\Carbon;
|
|
|
|
class BlockedIP extends Model
|
|
{
|
|
protected $table = 'ext_security_blocked_ips';
|
|
|
|
protected $fillable = [
|
|
'ip_address',
|
|
'reason',
|
|
'notes',
|
|
'is_permanent',
|
|
'blocked_at',
|
|
'blocked_until',
|
|
'blocked_by',
|
|
'unblocked_at',
|
|
'unblocked_by',
|
|
'is_active',
|
|
'failed_login_attempts',
|
|
'last_attempt_at',
|
|
];
|
|
|
|
protected $casts = [
|
|
'blocked_at' => 'datetime',
|
|
'blocked_until' => 'datetime',
|
|
'unblocked_at' => 'datetime',
|
|
'last_attempt_at' => 'datetime',
|
|
'is_permanent' => 'boolean',
|
|
'is_active' => 'boolean',
|
|
'failed_login_attempts' => 'integer',
|
|
];
|
|
|
|
public function blockedBy(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'blocked_by');
|
|
}
|
|
|
|
public function unblockedBy(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'unblocked_by');
|
|
}
|
|
|
|
/**
|
|
* Check if the IP block is currently active
|
|
*/
|
|
public function isCurrentlyActive(): bool
|
|
{
|
|
if (!$this->is_active) {
|
|
return false;
|
|
}
|
|
|
|
if ($this->is_permanent) {
|
|
return true;
|
|
}
|
|
|
|
if ($this->blocked_until && Carbon::now()->isAfter($this->blocked_until)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Unblock the IP
|
|
*/
|
|
public function unblock(?int $unblockedBy = null): void
|
|
{
|
|
$this->update([
|
|
'is_active' => false,
|
|
'unblocked_at' => now(),
|
|
'unblocked_by' => $unblockedBy,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get active block for an IP
|
|
*/
|
|
public static function getActiveBlock(string $ipAddress): ?self
|
|
{
|
|
return static::where('ip_address', $ipAddress)
|
|
->where('is_active', true)
|
|
->where(function ($query) {
|
|
$query->where('is_permanent', true)
|
|
->orWhere('blocked_until', '>', now())
|
|
->orWhereNull('blocked_until');
|
|
})
|
|
->first();
|
|
}
|
|
|
|
/**
|
|
* Check if an IP is blocked
|
|
*/
|
|
public static function isIPBlocked(string $ipAddress): bool
|
|
{
|
|
$block = static::getActiveBlock($ipAddress);
|
|
return $block && $block->isCurrentlyActive();
|
|
}
|
|
|
|
/**
|
|
* Block an IP address
|
|
*/
|
|
public static function blockIP(
|
|
string $ipAddress,
|
|
string $reason,
|
|
?int $blockedBy = null,
|
|
bool $isPermanent = false,
|
|
?Carbon $blockedUntil = null,
|
|
?string $notes = null
|
|
): self {
|
|
// Deactivate any existing blocks
|
|
static::where('ip_address', $ipAddress)
|
|
->where('is_active', true)
|
|
->update(['is_active' => false]);
|
|
|
|
return static::create([
|
|
'ip_address' => $ipAddress,
|
|
'reason' => $reason,
|
|
'notes' => $notes,
|
|
'is_permanent' => $isPermanent,
|
|
'blocked_at' => now(),
|
|
'blocked_until' => $blockedUntil,
|
|
'blocked_by' => $blockedBy,
|
|
'is_active' => true,
|
|
'failed_login_attempts' => 0,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Record a failed login attempt
|
|
*/
|
|
public static function recordFailedAttempt(string $ipAddress): void
|
|
{
|
|
$existing = static::where('ip_address', $ipAddress)
|
|
->where('is_active', true)
|
|
->first();
|
|
|
|
if ($existing) {
|
|
$existing->increment('failed_login_attempts');
|
|
$existing->update(['last_attempt_at' => now()]);
|
|
} else {
|
|
static::create([
|
|
'ip_address' => $ipAddress,
|
|
'reason' => 'Failed login attempts',
|
|
'is_permanent' => false,
|
|
'blocked_at' => now(),
|
|
'is_active' => false, // Not blocked yet, just tracking attempts
|
|
'failed_login_attempts' => 1,
|
|
'last_attempt_at' => now(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Auto-block IP after too many failed attempts
|
|
*/
|
|
public static function autoBlockIfNeeded(string $ipAddress, int $maxAttempts, int $blockDurationMinutes): bool
|
|
{
|
|
$record = static::where('ip_address', $ipAddress)->first();
|
|
|
|
if (!$record || $record->failed_login_attempts < $maxAttempts) {
|
|
return false;
|
|
}
|
|
|
|
// Block the IP
|
|
static::blockIP(
|
|
$ipAddress,
|
|
"Auto-blocked after {$maxAttempts} failed login attempts",
|
|
null, // System blocked
|
|
false, // Not permanent
|
|
now()->addMinutes($blockDurationMinutes)
|
|
);
|
|
|
|
return true;
|
|
}
|
|
}
|