Skip to content

Make host comparison case-insensitive, fix substring comparison #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/HostComparer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace webignition\Guzzle\Middleware\HttpAuthentication;

class HostComparer
{
public function isHostMatch(string $requestHost, string $authenticationHost)
{
$requestHost = strtolower($requestHost);
$authenticationHost = strtolower($authenticationHost);

return $requestHost === $authenticationHost
|| preg_match('*' . preg_quote($authenticationHost, '*') . '$*i', $requestHost) > 0;
}
}
19 changes: 12 additions & 7 deletions src/HttpAuthenticationMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

class HttpAuthenticationMiddleware
{
/**
* @var HostComparer
*/
private $hostComparer;

/**
* @var string
*/
Expand All @@ -14,13 +19,18 @@ class HttpAuthenticationMiddleware
/**
* @var string
*/
private $credentials = null;
private $credentials = '';

/**
* @var null string
*/
private $host = null;

public function __construct(HostComparer $hostComparer)
{
$this->hostComparer = $hostComparer;
}

public function setType(string $type)
{
$this->type = $type;
Expand Down Expand Up @@ -59,12 +69,7 @@ public function __invoke(callable $handler): callable
return $handler($request, $options);
}

$requestHost = $request->getHeaderLine('host');

$hasHostMatch = $requestHost === $this->host
&& preg_match('/' . preg_quote($this->host, '//') . '$/', $requestHost) > 0;

if (!$hasHostMatch) {
if (!$this->hostComparer->isHostMatch($request->getHeaderLine('host'), $this->host)) {
return $handler($request, $options);
}

Expand Down
60 changes: 60 additions & 0 deletions tests/HostComparerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php
/** @noinspection PhpDocSignatureInspection */

namespace webignition\Guzzle\Middleware\HttpAuthentication\Tests;

use webignition\Guzzle\Middleware\HttpAuthentication\HostComparer;

class HostComparerTest extends \PHPUnit\Framework\TestCase
{
/**
* @var HostComparer
*/
private $hostComparer;

protected function setUp()
{
parent::setUp();

$this->hostComparer = new HostComparer();
}

/**
* @dataProvider isHostMatchDataProvider
*/
public function testIsHostMatch(string $requestHost, string $authenticationHost, bool $expectedIsHostMatch)
{
$this->assertEquals($expectedIsHostMatch, $this->hostComparer->isHostMatch($requestHost, $authenticationHost));
}

public function isHostMatchDataProvider(): array
{
return [
'lowercase equality' => [
'requestHost' => 'example.com',
'authenticationHost' => 'example.com',
'expectedIsHostMatch' => true,
],
'uppercase equality' => [
'requestHost' => 'EXAMPLE.COM',
'authenticationHost' => 'EXAMPLE.COM',
'expectedIsHostMatch' => true,
],
'authentication host is ending substring of request host' => [
'requestHost' => 'subdomain.example.com',
'authenticationHost' => 'example.com',
'expectedIsHostMatch' => true,
],
'simple inequality' => [
'requestHost' => 'example.com',
'authenticationHost' => 'example.org',
'expectedIsHostMatch' => false,
],
'authentication host is middle substring of request host' => [
'requestHost' => 'subdomain.example.com.org',
'authenticationHost' => 'example.com',
'expectedIsHostMatch' => false,
],
];
}
}
13 changes: 5 additions & 8 deletions tests/HttpAuthenticationMiddlewareTest.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php
/** @noinspection PhpDocSignatureInspection */

namespace webignition\Guzzle\Middleware\HttpAuthentication\Tests;

Expand All @@ -7,6 +8,7 @@
use webignition\Guzzle\Middleware\HttpAuthentication\AuthorizationType;
use webignition\Guzzle\Middleware\HttpAuthentication\AuthorizationHeader;
use webignition\Guzzle\Middleware\HttpAuthentication\CredentialsFactory;
use webignition\Guzzle\Middleware\HttpAuthentication\HostComparer;
use webignition\Guzzle\Middleware\HttpAuthentication\HttpAuthenticationMiddleware;

class HttpAuthenticationMiddlewareTest extends \PHPUnit\Framework\TestCase
Expand All @@ -25,16 +27,11 @@ protected function setUp()
{
parent::setUp();

$this->httpAuthenticationMiddleware = new HttpAuthenticationMiddleware();
$this->httpAuthenticationMiddleware = new HttpAuthenticationMiddleware(new HostComparer());
}

/**
* @dataProvider invokeAuthorizationNotSetDataProvider
*
* @param string $requestHost
* @param string|null $type
* @param string|null $credentials
* @param string|null $host
*/
public function testInvokeAuthorizationNotSet(
string $requestHost,
Expand Down Expand Up @@ -181,13 +178,13 @@ function ($returnedRequest, $returnedOptions) use ($originalRequest, $options) {
/**
* @return MockInterface|RequestInterface
*/
private function createOriginalRequest(): RequestInterface
private function createOriginalRequest(string $host = self::HOST): RequestInterface
{
$request = \Mockery::mock(RequestInterface::class);
$request
->shouldReceive('getHeaderLine')
->with('host')
->andReturn(self::HOST);
->andReturn($host);

return $request;
}
Expand Down