Add all files
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\Security\Services;
|
||||
|
||||
use App\Models\User;
|
||||
use Paymenter\Extensions\Others\Security\Models\SuspendedAccount;
|
||||
use Paymenter\Extensions\Others\Security\Models\LoginAttempt;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class FraudDetectionService
|
||||
{
|
||||
public static function checkAndSuspendForFraud(User $user): bool
|
||||
{
|
||||
$extension = app(\Paymenter\Extensions\Others\Security\Security::class);
|
||||
$config = $extension->getConfig();
|
||||
|
||||
$autoSuspend = false;
|
||||
foreach ($config as $setting) {
|
||||
if ($setting['name'] === 'auto_suspend_fraud') {
|
||||
$autoSuspend = $setting['default'] ?? false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$autoSuspend) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fraudScore = static::calculateFraudScore($user);
|
||||
|
||||
if ($fraudScore >= 70) {
|
||||
SuspendedAccount::suspendUser(
|
||||
$user->id,
|
||||
"Auto-suspended due to suspected fraudulent activity (Score: {$fraudScore})",
|
||||
Auth::id() ?? 1,
|
||||
false,
|
||||
now()->addDays(7),
|
||||
"Automated fraud detection triggered suspension"
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function calculateFraudScore(User $user): int
|
||||
{
|
||||
$score = 0;
|
||||
|
||||
$recentFailedLogins = LoginAttempt::where('email', $user->email)
|
||||
->where('successful', false)
|
||||
->where('attempted_at', '>', now()->subDay())
|
||||
->count();
|
||||
|
||||
if ($recentFailedLogins > 10) $score += 30;
|
||||
elseif ($recentFailedLogins > 5) $score += 15;
|
||||
|
||||
$recentIPs = LoginAttempt::where('email', $user->email)
|
||||
->where('attempted_at', '>', now()->subHours(2))
|
||||
->distinct('ip_address')
|
||||
->count();
|
||||
|
||||
if ($recentIPs > 3) $score += 25;
|
||||
elseif ($recentIPs > 2) $score += 10;
|
||||
|
||||
$rapidLogins = LoginAttempt::where('email', $user->email)
|
||||
->where('attempted_at', '>', now()->subMinutes(10))
|
||||
->count();
|
||||
|
||||
if ($rapidLogins > 10) $score += 35;
|
||||
elseif ($rapidLogins > 5) $score += 20;
|
||||
|
||||
return min($score, 100);
|
||||
}
|
||||
|
||||
public static function shouldFlagForReview(User $user): bool
|
||||
{
|
||||
$fraudScore = static::calculateFraudScore($user);
|
||||
return $fraudScore >= 40 && $fraudScore < 70;
|
||||
}
|
||||
}
|
||||
123
extensions/Others/Security/Services/SecurityAuthService.php
Normal file
123
extensions/Others/Security/Services/SecurityAuthService.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\Security\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Paymenter\Extensions\Others\Security\Models\LoginAttempt;
|
||||
use Paymenter\Extensions\Others\Security\Models\BlockedIP;
|
||||
use Paymenter\Extensions\Others\Security\Models\SuspendedAccount;
|
||||
use Paymenter\Extensions\Others\Security\Services\VPNDetectionService;
|
||||
|
||||
class SecurityAuthService
|
||||
{
|
||||
public static function attemptLogin(array $credentials, bool $remember = false, string $ipAddress = null): array
|
||||
{
|
||||
$email = $credentials['email'] ?? '';
|
||||
$ipAddress = $ipAddress ?? request()->ip();
|
||||
|
||||
if (BlockedIP::isIPBlocked($ipAddress)) {
|
||||
$block = BlockedIP::getActiveBlock($ipAddress);
|
||||
|
||||
LoginAttempt::recordAttempt(
|
||||
$ipAddress,
|
||||
$email,
|
||||
false,
|
||||
request()->userAgent(),
|
||||
null,
|
||||
'Login blocked - IP blocked: ' . $block->reason
|
||||
);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Your IP address has been blocked. Reason: ' . $block->reason,
|
||||
'type' => 'ip_blocked'
|
||||
];
|
||||
}
|
||||
|
||||
if (VPNDetectionService::checkAndBlockVPN($ipAddress, $email)) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Access denied. VPN/Proxy connections are not allowed.',
|
||||
'type' => 'vpn_blocked'
|
||||
];
|
||||
}
|
||||
|
||||
$user = \App\Models\User::where('email', $email)->first();
|
||||
if ($user && SuspendedAccount::isUserSuspended($user->id)) {
|
||||
$suspension = SuspendedAccount::getActiveSuspension($user->id);
|
||||
|
||||
LoginAttempt::recordAttempt(
|
||||
$ipAddress,
|
||||
$email,
|
||||
false,
|
||||
request()->userAgent(),
|
||||
null,
|
||||
'Login blocked - Account suspended: ' . $suspension->reason
|
||||
);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Your account has been suspended. Reason: ' . $suspension->reason,
|
||||
'type' => 'account_suspended'
|
||||
];
|
||||
}
|
||||
|
||||
if (!Auth::attempt($credentials, $remember)) {
|
||||
LoginAttempt::recordAttempt(
|
||||
$ipAddress,
|
||||
$email,
|
||||
false,
|
||||
request()->userAgent(),
|
||||
null,
|
||||
'Failed login attempt - Invalid credentials'
|
||||
);
|
||||
|
||||
BlockedIP::recordFailedAttempt($ipAddress);
|
||||
static::autoBlockIfNeeded($ipAddress);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'These credentials do not match our records.',
|
||||
'type' => 'invalid_credentials'
|
||||
];
|
||||
}
|
||||
|
||||
LoginAttempt::recordAttempt(
|
||||
$ipAddress,
|
||||
$email,
|
||||
true,
|
||||
request()->userAgent(),
|
||||
null,
|
||||
'Successful login'
|
||||
);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Login successful',
|
||||
'type' => 'success'
|
||||
];
|
||||
}
|
||||
|
||||
protected static function autoBlockIfNeeded(string $ipAddress): void
|
||||
{
|
||||
try {
|
||||
$extension = app(\Paymenter\Extensions\Others\Security\Security::class);
|
||||
$config = $extension->getConfig();
|
||||
|
||||
$maxAttempts = 5;
|
||||
$blockDuration = 60;
|
||||
|
||||
foreach ($config as $setting) {
|
||||
if ($setting['name'] === 'max_login_attempts') {
|
||||
$maxAttempts = $setting['default'] ?? 5;
|
||||
}
|
||||
if ($setting['name'] === 'ip_block_duration') {
|
||||
$blockDuration = $setting['default'] ?? 60;
|
||||
}
|
||||
}
|
||||
|
||||
BlockedIP::autoBlockIfNeeded($ipAddress, $maxAttempts, $blockDuration);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
180
extensions/Others/Security/Services/VPNDetectionService.php
Normal file
180
extensions/Others/Security/Services/VPNDetectionService.php
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\Security\Services;
|
||||
|
||||
use Paymenter\Extensions\Others\Security\Models\BlockedIP;
|
||||
use Paymenter\Extensions\Others\Security\Models\LoginAttempt;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class VPNDetectionService
|
||||
{
|
||||
public static function checkAndBlockVPN(string $ipAddress, string $email = null, string $action = 'login'): bool
|
||||
{
|
||||
$extension = app(\Paymenter\Extensions\Others\Security\Security::class);
|
||||
$config = $extension->getConfig();
|
||||
|
||||
$blockVPNs = false;
|
||||
foreach ($config as $setting) {
|
||||
if ($setting['name'] === 'block_vpn_ips') {
|
||||
$blockVPNs = $setting['default'] ?? false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$blockVPNs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cacheKey = "vpn_check_{$ipAddress}";
|
||||
$isVPN = Cache::get($cacheKey);
|
||||
|
||||
if ($isVPN === null) {
|
||||
$isVPN = static::detectVPN($ipAddress);
|
||||
Cache::put($cacheKey, $isVPN, now()->addHours(6));
|
||||
}
|
||||
|
||||
if ($isVPN) {
|
||||
LoginAttempt::recordAttempt(
|
||||
$ipAddress,
|
||||
$email,
|
||||
false,
|
||||
request()->userAgent(),
|
||||
null,
|
||||
"Blocked VPN/Proxy IP detected during {$action}"
|
||||
);
|
||||
|
||||
BlockedIP::blockIP(
|
||||
$ipAddress,
|
||||
'Auto-blocked VPN/Proxy IP address',
|
||||
null,
|
||||
false,
|
||||
now()->addDays(1),
|
||||
'Detected as VPN/Proxy service'
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function detectVPN(string $ipAddress): bool
|
||||
{
|
||||
if (static::isPrivateIP($ipAddress)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (static::isKnownVPNRange($ipAddress)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (static::checkWithFreeAPI($ipAddress)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function isPrivateIP(string $ipAddress): bool
|
||||
{
|
||||
return !filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
|
||||
}
|
||||
|
||||
protected static function isKnownVPNRange(string $ipAddress): bool
|
||||
{
|
||||
$knownVPNRanges = [
|
||||
'185.220.', // Tor exit nodes
|
||||
'199.87.154.', // NordVPN
|
||||
'103.216.60.', // ExpressVPN
|
||||
'146.70.', // Surfshark
|
||||
'91.219.', // CyberGhost
|
||||
'185.159.', // ProtonVPN
|
||||
'104.244.', // Private Internet Access
|
||||
'149.154.', // Windscribe
|
||||
'193.148.', // IPVanish
|
||||
'185.246.', // Hide.me
|
||||
];
|
||||
|
||||
foreach ($knownVPNRanges as $range) {
|
||||
if (str_starts_with($ipAddress, $range)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function checkWithFreeAPI(string $ipAddress): bool
|
||||
{
|
||||
try {
|
||||
$response = Http::timeout(5)->get("http://ip-api.com/json/{$ipAddress}", [
|
||||
'fields' => 'proxy,hosting,query'
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
$data = $response->json();
|
||||
if (isset($data['proxy']) && $data['proxy']) {
|
||||
return true;
|
||||
}
|
||||
if (isset($data['hosting']) && $data['hosting']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::warning('VPN detection API failed', ['ip' => $ipAddress, 'error' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
try {
|
||||
$response = Http::timeout(5)->get("https://vpnapi.io/api/{$ipAddress}", [
|
||||
'key' => 'free'
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
$data = $response->json();
|
||||
if (isset($data['security']['vpn']) && $data['security']['vpn']) {
|
||||
return true;
|
||||
}
|
||||
if (isset($data['security']['proxy']) && $data['security']['proxy']) {
|
||||
return true;
|
||||
}
|
||||
if (isset($data['security']['tor']) && $data['security']['tor']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::warning('VPN detection API failed', ['ip' => $ipAddress, 'error' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function isVPNBlocked(string $ipAddress): bool
|
||||
{
|
||||
$extension = app(\Paymenter\Extensions\Others\Security\Security::class);
|
||||
$config = $extension->getConfig();
|
||||
|
||||
$blockVPNs = false;
|
||||
foreach ($config as $setting) {
|
||||
if ($setting['name'] === 'block_vpn_ips') {
|
||||
$blockVPNs = $setting['default'] ?? false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$blockVPNs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cacheKey = "vpn_check_{$ipAddress}";
|
||||
$isVPN = Cache::get($cacheKey);
|
||||
|
||||
if ($isVPN === null) {
|
||||
$isVPN = static::detectVPN($ipAddress);
|
||||
Cache::put($cacheKey, $isVPN, now()->addHours(6));
|
||||
}
|
||||
|
||||
return $isVPN;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user