Last active
May 20, 2020 05:35
-
-
Save scott1702/9b666d1c18136bd6f654f617aefa7e64 to your computer and use it in GitHub Desktop.
Whitelist Swiftype by User Agent, based on implementation from https://github.com/silverstripe/cwp-core
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
Name: basicauth | |
--- | |
SilverStripe\Core\Injector\Injector: | |
SilverStripe\Security\BasicAuthMiddleware: | |
class: 'App\Security\CustomBasicAuthMiddleware' | |
properties: | |
# Inject customisable IP whitelist and User agent | |
WhitelistedIps: '127.0.0.1,127.0.0.2' | |
WhitelistedUserAgents: "`SWIFTYPE_USER_AGENT`" | |
--- | |
Name: basicauth_uat | |
Only: | |
environment: test | |
--- | |
SilverStripe\Core\Injector\Injector: | |
SilverStripe\Security\BasicAuthMiddleware: | |
properties: | |
# Enforce basic authentication in UAT environments for all routes except for the "change password" form | |
URLPatterns: | |
'#^Security/lostpassword#i': false | |
'#^Security/changepassword#i': false | |
'#.*#': true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Security; | |
use SilverStripe\Control\HTTPRequest; | |
use SilverStripe\Security\BasicAuthMiddleware; | |
class CustomBasicAuthMiddleware extends BasicAuthMiddleware | |
{ | |
/** | |
* Whitelisted IP addresses will not be given a basic authentication prompt when other basic authentication | |
* rules via {@link BasicAuthMiddleware} are enabled. | |
* | |
* Please note that this will not have any effect if using BasicAuth.entire_site_protected, which will | |
* always enabled basic authentication for the entire site. | |
* | |
* @var array | |
*/ | |
protected $whitelistedIps = []; | |
protected $whitelistedUserAgents = []; | |
/** | |
* @return array | |
*/ | |
public function getWhitelistedIps() | |
{ | |
return $this->whitelistedIps; | |
} | |
/** | |
* @return array | |
*/ | |
public function getWhitelistedUserAgents() | |
{ | |
return $this->whitelistedUserAgents; | |
} | |
/** | |
* @param string|string[] $whitelistedIps An array of IP addresses, a comma delimited string, or an array of IPs | |
* or comma delimited IP list strings | |
* @return $this | |
*/ | |
public function setWhitelistedIps($whitelistedIps) | |
{ | |
// Allow string or array input | |
$ipLists = is_array($whitelistedIps) ? $whitelistedIps : [$whitelistedIps]; | |
$whitelistedIps = []; | |
// Break each string in the array by commas to support nested IP lists | |
foreach ($ipLists as $ipList) { | |
if (!$ipList) { | |
continue; | |
} | |
$ips = array_map('trim', explode(',', $ipList)); | |
$whitelistedIps = array_merge($whitelistedIps, $ips); | |
} | |
// Return unique values with keys reset | |
$this->whitelistedIps = array_values(array_unique($whitelistedIps)); | |
return $this; | |
} | |
/** | |
* @param string|string[] $whitelistedUserAgents An array of user agents, a pipe (|) delimited string, or an array of IPs | |
* or comma delimited IP list strings | |
* @return $this | |
*/ | |
public function setWhitelistedUserAgents($whitelistedUserAgents) | |
{ | |
// Allow string or array input | |
$userAgentLists = is_array($whitelistedUserAgents) ? $whitelistedUserAgents : [$whitelistedUserAgents]; | |
$whitelistedUserAgents = []; | |
// Break each string in the array by commas to support nested IP lists | |
foreach ($userAgentLists as $userAgentList) { | |
if (!$userAgentList) { | |
continue; | |
} | |
$userAgents = array_map('trim', explode('|', $userAgentList)); | |
$whitelistedUserAgents = array_merge($whitelistedUserAgents, $userAgents); | |
} | |
// Return unique values with keys reset | |
$this->whitelistedUserAgents = array_values(array_unique($whitelistedUserAgents)); | |
return $this; | |
} | |
/** | |
* Check for any whitelisted IP addresses. If one matches the current user's IP then return false early, | |
* otherwise allow the default {@link BasicAuthMiddleware} to continue its logic. | |
* | |
* {@inheritDoc} | |
*/ | |
protected function checkMatchingURL(HTTPRequest $request) | |
{ | |
if ($this->ipMatchesWhitelist() || $this->userAgentMatchesWhitelist()) { | |
return false; | |
} | |
return parent::checkMatchingURL($request); | |
} | |
/** | |
* Check whether the current user's IP address is in the IP whitelist | |
* | |
* @return bool | |
*/ | |
protected function ipMatchesWhitelist() | |
{ | |
$whitelist = $this->getWhitelistedIps(); | |
// Continue if no whitelist is defined | |
if (empty($whitelist)) { | |
return false; | |
} | |
$userIp = $_SERVER['REMOTE_ADDR']; | |
if (in_array($userIp, $whitelist)) { | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Check whether the current user's user agent is in the whitelist | |
* | |
* @return bool | |
*/ | |
protected function userAgentMatchesWhitelist() | |
{ | |
$whitelist = $this->getWhitelistedUserAgents(); | |
// Continue if no whitelist is defined | |
if (empty($whitelist)) { | |
return false; | |
} | |
$userAgent = $_SERVER['HTTP_USER_AGENT']; | |
if (in_array($userAgent, $whitelist)) { | |
return true; | |
} | |
return false; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Test; | |
use SilverStripe\Control\HTTPRequest; | |
use SilverStripe\Control\HTTPResponse; | |
use SilverStripe\Core\Config\Config; | |
use SilverStripe\Core\Injector\Injector; | |
use SilverStripe\Dev\SapphireTest; | |
use SilverStripe\Security\BasicAuth; | |
use SilverStripe\Security\BasicAuthMiddleware; | |
use App\Security\CustomBasicAuthMiddleware; | |
class CustomBasicAuthMiddlewareTest extends SapphireTest | |
{ | |
/** | |
* @var CustomBasicAuthMiddleware | |
*/ | |
protected $middleware; | |
/** | |
* @var array | |
*/ | |
protected $originalServersVars = []; | |
protected function setUp() | |
{ | |
parent::setUp(); | |
$this->middleware = Injector::inst()->get(BasicAuthMiddleware::class); | |
$this->originalServersVars = $_SERVER; | |
Config::modify()->set(BasicAuth::class, 'ignore_cli', false); | |
} | |
protected function tearDown() | |
{ | |
$_SERVER = $this->originalServersVars; | |
parent::tearDown(); | |
} | |
public function testSetWhitelistedIpsAcceptsStrings() | |
{ | |
$this->middleware->setWhitelistedIps('127.0.0.1,127.0.0.2'); | |
$this->assertSame([ | |
'127.0.0.1', | |
'127.0.0.2', | |
], $this->middleware->getWhitelistedIps(), 'Accepts comma delimited strings'); | |
} | |
public function testSetWhitelistedIpsAcceptsArraysOfStrings() | |
{ | |
$this->middleware->setWhitelistedIps(['127.0.0.1']); | |
$this->assertSame(['127.0.0.1'], $this->middleware->getWhitelistedIps(), 'Accepts array values'); | |
} | |
public function testSetWhitelistedIpsSupportedNestedStringListsInsideArrays() | |
{ | |
$this->middleware->setWhitelistedIps([ | |
'127.0.0.1,127.0.0.2', | |
' 137.0.0.1 , 127.0.0.2', | |
'127.0.0.3', | |
'127.0.0.3', // check results are unique | |
'127.0.0.4', | |
]); | |
$this->assertSame([ | |
'127.0.0.1', | |
'127.0.0.2', | |
'137.0.0.1', | |
'127.0.0.3', | |
'127.0.0.4', | |
], $this->middleware->getWhitelistedIps(), 'Accepts IP list strings inside arrays'); | |
} | |
/** | |
* @param string $currentIp | |
* @param int $expected | |
* @dataProvider whitelistingProvider | |
*/ | |
public function testIpWhitelisting($currentIp, $expected) | |
{ | |
// Enable basic auth everywhere | |
$this->middleware->setURLPatterns(['#.*#' => true]); | |
// Set a whitelisted IP address | |
$_SERVER['REMOTE_ADDR'] = $currentIp; | |
$this->middleware->setWhitelistedIps(['127.0.0.1']); | |
$response = $this->mockRequest(); | |
$this->assertEquals($expected, $response->getStatusCode()); | |
} | |
/** | |
* @return array[] | |
*/ | |
public function whitelistingProvider() | |
{ | |
return [ | |
'IP not in whitelist' => ['123.456.789.012', 401], | |
'IP in whitelist' => ['127.0.0.1', 200], | |
]; | |
} | |
/** | |
* Perform a mock middleware request. Will return 200 if everything is OK. | |
* | |
* @param string $url | |
* @return HTTPResponse | |
*/ | |
protected function mockRequest($url = '/foo') | |
{ | |
$request = new HTTPRequest('GET', $url); | |
return $this->middleware->process($request, function () { | |
return new HTTPResponse('OK', 200); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment