src/Services/Security/IpTools.php line 25

Open in your IDE?
  1. <?php
  2. //----------------------------------------------------------------------
  3. // src/Services/Security/IpTools.php
  4. //----------------------------------------------------------------------
  5. namespace App\Services\Security;
  6. use Doctrine\Persistence\ManagerRegistry;
  7. use Symfony\Component\HttpFoundation\RequestStack;
  8. use App\Entity\Security\IpAttempt;
  9. use App\Entity\Security\IpBan;
  10. use App\Entity\Security\Whitelist;
  11. use App\Services\LogTools;
  12. class IpTools
  13. {
  14.     const MAX_LOGIN_FAILURE_ATTEMPTS 25;
  15.     const ATTEMPTS_INTERVAL_SECONDS 60 10;
  16.     const BAN_TIME_SECONDS 60 10;
  17.     public function __construct(ManagerRegistry $doctrineRequestStack $requestStackLogTools $logTools)
  18.     {
  19.         $this->em $doctrine->getManager();
  20.         $this->requestStack $requestStack;
  21.         $this->logTools $logTools;
  22.     }
  23.     public function fail2ban($args)
  24.     {
  25.         $request $this->requestStack->getCurrentRequest();
  26.         if ($request === null)
  27.         {
  28.             return false;
  29.         }
  30.         if (empty($request->getClientIp()))
  31.         {
  32.             return false;
  33.         }
  34.         // Plan.io Task #3696 : Whitelisting
  35.         // Get the client IP from the Request Stack
  36.         $whitelistIps $this->em->getRepository(Whitelist::class)
  37.             ->getForIp($request->getClientIp());
  38.         if (count($whitelistIps))
  39.         {
  40.             // IP is whitelisted
  41.             // $this->logTools->ploopLog("[IpTools][fail2ban] IP ".$request->getClientIp()." is whitelisted");
  42.             return true;
  43.         }
  44.         // Various initialisations
  45.         $username null;
  46.         if (array_key_exists('username'$args))
  47.         {
  48.             $username $args['username'];
  49.         }
  50.         $errors = array();
  51.         // FormErrors
  52.         if (array_key_exists('form_errors'$args))
  53.         {
  54.             foreach ($args['form_errors'] as $key => $error)
  55.             {
  56.                 if ($error->getCause() !== null)
  57.                 {
  58.                     $errors[] = $error->getCause()->getMessageTemplate();
  59.                 }
  60.                 else
  61.                 {
  62.                     $errors[] = $error->getMessage();
  63.                 }
  64.             }
  65.         }
  66.         // Errors, in general
  67.         if (array_key_exists('error'$args))
  68.         {
  69.             $errors[] = $args['error'];
  70.         }
  71.         // Make only one error field
  72.         $errorDisplay null;
  73.         if (count($errors) == 1)
  74.         {
  75.             $errorDisplay $errors[0];
  76.         }
  77.         else
  78.         {
  79.             if (count($errors) > 1)
  80.             {
  81.                 $errorDisplay "";
  82.                 foreach ($errors as $error)
  83.                 {
  84.                     $errorDisplay .= $error;
  85.                     $errorDisplay .= ", ";
  86.                 }
  87.                 $errorDisplay substr($errorDisplay,0,-1);
  88.                 $errorDisplay substr($errorDisplay,0,-1);
  89.             }
  90.         }
  91.         // Get the client IP from the Request Stack
  92.         $ip $request->getClientIp();
  93.         // Get the lastest (according to MAX_LOGIN_FAILURE_ATTEMPTS) attempts
  94.         $ipAttempts $this->em
  95.             ->getRepository(IpAttempt::class)
  96.             ->getLatest($ipself::MAX_LOGIN_FAILURE_ATTEMPTS);
  97.         $ipAttempts array_reverse($ipAttempts);
  98.         // Count them
  99.         $attempts count($ipAttempts);
  100.         // If we have less than the maximum allowed, do nothing
  101.         if ($attempts >= self::MAX_LOGIN_FAILURE_ATTEMPTS)
  102.         {
  103.             // Get oldest entry
  104.             $oldest $ipAttempts[0]->getCreationDate();
  105.             if ($oldest !== null)
  106.             {
  107.                 // Compare it to the present
  108.                 $now = new \DateTime();
  109.                 // Get diff btw old/now in seconds
  110.                 $diff $now->getTimestamp() - $oldest->getTimestamp();
  111.                 // If too recent, ban the ip
  112.                 if ($diff self::ATTEMPTS_INTERVAL_SECONDS)
  113.                 {
  114.                     $ipBan = new IpBan();
  115.                     $ipBan->setIp($ip);
  116.                     $ipBan->setEndDate($now->add(new \DateInterval('PT'.self::BAN_TIME_SECONDS.'S')));
  117.                     $this->em->persist($ipBan);
  118.                 }
  119.             }
  120.         }
  121.         // In all cases log new attempt
  122.         $attempt = new IpAttempt();
  123.         $attempt->setIp($ip);
  124.         $attempt->setUsername($username);
  125.         $attempt->setError($errorDisplay);
  126.         $attempt->setUri($request->getUri());
  127.         $this->em->persist($attempt);
  128.         try
  129.         {
  130.             $this->em->flush();
  131.         }
  132.         catch (\Exception $e)
  133.         {
  134.             // Something went bad
  135.             $this->logTools->errorLog($e->getMessage());
  136.             return false;
  137.         }
  138.         return true;
  139.     }
  140.     public function isBanned($ip)
  141.     {
  142.         $ipBan $this->em
  143.             ->getRepository(IpBan::class)
  144.             ->findOneBy(
  145.                 array('ip'    =>    $ip),
  146.                 array('endDate'    =>    'DESC',)
  147.             );
  148.         if ($ipBan === null)
  149.         {
  150.             return false;
  151.         }
  152.         // Check timestamp
  153.         $now = new \DateTime();
  154.         if ($now->format("YmdHis") <= $ipBan->getEndDate()->format("YmdHis"))
  155.         {
  156.             return true;
  157.         }
  158.         return false;
  159.     }
  160. }