[SecurityBundle] [HttpFoundation] Allow for multiple IP address in security access_control rules

This commit is contained in:
Dan Finnie 2013-03-02 22:49:25 -05:00 committed by Fabien Potencier
parent 54b32f1a4a
commit 650352d1ca
6 changed files with 75 additions and 10 deletions

View File

@ -152,6 +152,7 @@ class MainConfiguration implements ConfigurationInterface
->arrayNode('access_control')
->cannotBeOverwritten()
->prototype('array')
->fixXmlConfig('ip')
->children()
->scalarNode('requires_channel')->defaultNull()->end()
->scalarNode('path')
@ -160,7 +161,10 @@ class MainConfiguration implements ConfigurationInterface
->example('^/path to resource/')
->end()
->scalarNode('host')->defaultNull()->end()
->scalarNode('ip')->defaultNull()->end()
->arrayNode('ips')
->beforeNormalization()->ifString()->then(function($v) { return array($v); })->end()
->prototype('scalar')->end()
->end()
->arrayNode('methods')
->beforeNormalization()->ifString()->then(function($v) { return preg_split('/\s*,\s*/', $v); })->end()
->prototype('scalar')->end()

View File

@ -184,7 +184,7 @@ class SecurityExtension extends Extension
$access['path'],
$access['host'],
$access['methods'],
$access['ip']
$access['ips']
);
$container->getDefinition('security.access_map')

View File

@ -25,6 +25,12 @@ form_login_redirect_to_protected_resource_after_login:
highly_protected_resource:
path: /highly_protected_resource
secured-by-one-ip:
path: /secured-by-one-ip
secured-by-two-ips:
path: /secured-by-two-ips
form_logout:
path: /logout_path

View File

@ -63,6 +63,44 @@ class SecurityRoutingIntegrationTest extends WebTestCase
$this->assertNotEquals(404, $client->getResponse()->getStatusCode());
}
/**
* @dataProvider getConfigs
* @group ip_whitelist
*/
public function testSecurityConfigurationForSingleIPAddress($config)
{
$allowedClient = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config), array("REMOTE_ADDR" => "10.10.10.10"));
$barredClient = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config), array("REMOTE_ADDR" => "10.10.20.10"));
$this->assertAllowed($allowedClient, '/secured-by-one-ip');
$this->assertRestricted($barredClient, '/secured-by-one-ip');
}
/**
* @dataProvider getConfigs
* @group ip_whitelist
*/
public function testSecurityConfigurationForMultipleIPAddresses($config)
{
$allowedClientA = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config), array("REMOTE_ADDR" => "1.1.1.1"));
$allowedClientB = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config), array("REMOTE_ADDR" => "2.2.2.2"));
$barredClient = $this->createClient(array('test_case' => 'StandardFormLogin', 'root_config' => $config), array("REMOTE_ADDR" => "192.168.1.1"));
$this->assertAllowed($allowedClientA, '/secured-by-two-ips');
$this->assertAllowed($allowedClientB, '/secured-by-two-ips');
$this->assertRestricted($barredClient, '/secured-by-two-ips');
}
private function assertAllowed($client, $path) {
$client->request('GET', $path);
$this->assertEquals(404, $client->getResponse()->getStatusCode());
}
private function assertRestricted($client, $path) {
$client->request('GET', $path);
$this->assertEquals(302, $client->getResponse()->getStatusCode());
}
public function getConfigs()
{
return array(array('config.yml'), array('routes_as_path.yml'));

View File

@ -28,5 +28,7 @@ security:
access_control:
- { path: ^/unprotected_resource$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/secure-but-not-covered-by-access-control$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/secured-by-one-ip$, ip: 10.10.10.10, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/secured-by-two-ips$, ips: [1.1.1.1, 2.2.2.2], roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/highly_protected_resource$, roles: IS_ADMIN }
- { path: .*, roles: IS_AUTHENTICATED_FULLY }

View File

@ -38,7 +38,7 @@ class RequestMatcher implements RequestMatcherInterface
/**
* @var string
*/
private $ip;
private $ips = array();
/**
* @var array
@ -49,15 +49,15 @@ class RequestMatcher implements RequestMatcherInterface
* @param string|null $path
* @param string|null $host
* @param string|string[]|null $methods
* @param string|null $ip
* @param string|string[]|null $ips
* @param array $attributes
*/
public function __construct($path = null, $host = null, $methods = null, $ip = null, array $attributes = array())
public function __construct($path = null, $host = null, $methods = null, $ips = null, array $attributes = array())
{
$this->matchPath($path);
$this->matchHost($host);
$this->matchMethod($methods);
$this->matchIp($ip);
$this->matchIps($ips);
foreach ($attributes as $k => $v) {
$this->matchAttribute($k, $v);
}
@ -90,7 +90,17 @@ class RequestMatcher implements RequestMatcherInterface
*/
public function matchIp($ip)
{
$this->ip = $ip;
$this->matchIps($ip);
}
/**
* Adds a check for the client IP.
*
* @param string|string[] $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24
*/
public function matchIps($ips)
{
$this->ips = (array) $ips;
}
/**
@ -143,10 +153,15 @@ class RequestMatcher implements RequestMatcherInterface
return false;
}
if (null !== $this->ip && !IpUtils::checkIp($request->getClientIp(), $this->ip)) {
return false;
foreach($this->ips as $ip) {
if (IpUtils::checkIp($request->getClientIp(), $ip)) {
return true;
}
}
return true;
// Note to future implementors: add additional checks above the
// foreach above or else your check might not be run!
return count($this->ips) === 0;
}
}