<?php
/**
* Created by PhpStorm.
* User: Ninon
* Date: 22/03/2017
* Time: 15:16
*/
namespace App\Security\Authorization\Voter;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class ClientIpVoter extends Voter
{
const ALLOW = 'allow';
const DENY = 'deny';
/**
* @var array
*/
protected $_sListeIPs;
/**
* ClientIpVoter constructor.
*
* @param array $aListeIPs
*/
public function __construct(array $aListeIPs)
{
$this->_sListeIPs = $aListeIPs;
}
/**
* Determines if the attribute and subject are supported by this voter.
*
* @param string $attribute An attribute
* @param mixed $subject The subject to secure, e.g. an object the user wants to access or any other PHP type
*
* @return bool True if the attribute and subject are supported, false otherwise
*/
protected function supports(string $attribute, $subject)
{
// if the attribute isn't one we support, return false
if (!in_array($attribute, [self::ALLOW, self::DENY])) {
return false;
}
// only vote on `Request` objects
if (!$subject instanceof Request) {
return false;
}
return true;
}
/**
* Perform a single access check operation on a given attribute, subject and token.
* It is safe to assume that $attribute and $subject already passed the "supports()" method check.
*
* @param mixed $subject
*
* @return bool
*/
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token)
{
// you know $subject is a Post object, thanks to `supports()`
/** @var Request $request */
$request = $subject;
$route = $request->get('_route');
if ($route === 'admin_noutonline_version'){
switch ($attribute)
{
case self::ALLOW:
return true;
case self::DENY:
return false;
}
}
$sClientIP = $request->getClientIp();
$bMatche = false;
foreach($this->_sListeIPs as $range)
{
if ($this->_bTestIP($sClientIP, $range)){
$bMatche = true;
break;
}
}
switch ($attribute)
{
case self::ALLOW:
return $bMatche;
case self::DENY:
return !$bMatche;
}
return false;
}
/**
* transforme une ip en chaine binaire
* @param $sIP
* @return string
*/
protected function _sIP2bits($sIP)
{
$cx = strpos($sIP, '/');
if (!$cx)
{
$in_addr = inet_pton($sIP);
return bin2hex($in_addr);
}
// Strip out the netmask, if there is one.
$subnet = (int)(substr($sIP, $cx+1));
$ipaddr = substr($sIP, 0, $cx);
$in_addr = inet_pton($ipaddr);
$aByteAddr = str_split($in_addr);
// Maximum netmask length = same as packed address
$len = 8*strlen($in_addr);
if ($subnet > $len){
$subnet = $len;
}
// Create a hex expression of the subnet mask
$mask = str_repeat('f', $subnet>>2);
switch($subnet & 3)
{
case 3: $mask .= 'e'; break;
case 2: $mask .= 'c'; break;
case 1: $mask .= '8'; break;
}
$mask = str_pad($mask, $len>>2, '0');
// Packed representation of netmask
$mask = pack('H*', $mask);
$aByteMask = str_split($mask);
$sIpRes = '';
for ($i=0 ; $i<count($aByteAddr) ; $i++)
{
$nValIp = ord($aByteAddr[$i]);
$nValMasque = isset($aByteMask[$i]) ? ord($aByteMask[$i]) : 0;
$sIpRes.= str_pad(dechex($nValIp & $nValMasque), 2, '0', STR_PAD_LEFT);
}
return $sIpRes;
}
/**
* Compare l'ip du client avec le range
* @param $clientIP
* @param $range
* @return bool
*/
protected function _bTestIP($clientIP, $range)
{
$cx = strpos($range, '/');
if ($cx){
$clientIP .= substr($range, $cx);
}
$sBinClientIp = $this->_sIP2bits($clientIP);
$sBinRange = $this->_sIP2bits($range);
return strcasecmp($sBinClientIp, $sBinRange)==0;
}
}