Add all files
This commit is contained in:
181
extensions/Others/Security/Models/BlockedIP.php
Normal file
181
extensions/Others/Security/Models/BlockedIP.php
Normal file
@@ -0,0 +1,181 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user