diff --git a/src/Escher/Escher.php b/src/Escher/Escher.php index 5947c40..68f6583 100644 --- a/src/Escher/Escher.php +++ b/src/Escher/Escher.php @@ -18,7 +18,6 @@ class Escher const UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD'; private $credentialScope; - private $date; private $clockSkew = self::DEFAULT_CLOCK_SKEW; private $hashAlgo = self::DEFAULT_HASH_ALGORITHM; private $algoPrefix = self::DEFAULT_ALGO_PREFIX; @@ -26,15 +25,14 @@ class Escher private $authHeaderKey = self::DEFAULT_AUTH_HEADER_KEY; private $dateHeaderKey = self::DEFAULT_DATE_HEADER_KEY; - public function __construct($credentialScope, \DateTime $date) + public function __construct($credentialScope) { $this->credentialScope = $credentialScope; - $this->date = $date; } - public static function create($credentialScope, \DateTime $date = null) + public static function create($credentialScope) { - return new Escher($credentialScope, $date ? $date : self::now()); + return new Escher($credentialScope); } /** @@ -45,6 +43,13 @@ private static function now() return new \DateTime('now', new \DateTimeZone('GMT')); } + /** + * @param $keyDB + * @param array|null $serverVars + * @param null $requestBody + * @return mixed + * @throws Exception + */ public function authenticate($keyDB, array $serverVars = null, $requestBody = null) { $serverVars = null === $serverVars ? $_SERVER : $serverVars; @@ -63,15 +68,16 @@ public function authenticate($keyDB, array $serverVars = null, $requestBody = nu return $authElements->getAccessKeyId(); } - public function presignUrl($accessKeyId, $secretKey, $url, $expires = Escher::DEFAULT_EXPIRES) + public function presignUrl($accessKeyId, $secretKey, $url, $expires = Escher::DEFAULT_EXPIRES, \DateTime $date = null) { - $url = $this->appendSigningParams($accessKeyId, $url, $this->date, $expires); + $date = $date ? $date : self::now(); + $url = $this->appendSigningParams($accessKeyId, $url, $date, $expires); list($host, $path, $query) = $this->parseUrl($url); $signature = $this->calculateSignature( $secretKey, - $this->date, + $date, 'GET', $path, $query, @@ -84,18 +90,19 @@ public function presignUrl($accessKeyId, $secretKey, $url, $expires = Escher::DE return $url; } - public function signRequest($accessKeyId, $secretKey, $method, $url, $requestBody, $headerList = array(), $headersToSign = array()) + public function signRequest($accessKeyId, $secretKey, $method, $url, $requestBody, $headerList = array(), $headersToSign = array(), \DateTime $date = null) { + $date = $date ? $date : self::now(); list($host, $path, $query) = $this->parseUrl($url); list($headerList, $headersToSign) = $this->addMandatoryHeaders( - $headerList, $headersToSign, $this->dateHeaderKey, $this->date, $host + $headerList, $headersToSign, $this->dateHeaderKey, $date, $host ); return $headerList + $this->generateAuthHeader( $secretKey, $accessKeyId, $this->authHeaderKey, - $this->date, + $date, $method, $path, $query, diff --git a/test/unit/AuthenticateRequestTest.php b/test/unit/AuthenticateRequestTest.php index de31c17..3d22156 100644 --- a/test/unit/AuthenticateRequestTest.php +++ b/test/unit/AuthenticateRequestTest.php @@ -8,6 +8,7 @@ class AuthenticateRequestTest extends TestBase { /** * @test + * @throws Exception */ public function itShouldAuthenticateRequestUsingAuthHeader() { @@ -32,6 +33,12 @@ public function itShouldAuthenticateRequestUsingAuthHeader() /** * @test * @dataProvider validPortProvider + * @param $httpHost + * @param $serverName + * @param $serverPort + * @param $https + * @param $signature + * @throws Exception */ public function itShouldAuthenticateRequestRegardlessDefaultPortProvidedOrNot($httpHost, $serverName, $serverPort, $https, $signature) { @@ -70,6 +77,11 @@ public function validPortProvider() /** * @test * @dataProvider requestTamperingProvider + * @param $tamperedKey + * @param $tamperedValue + * @param $expectedErrorMessage + * @param $expectedErrorCode + * @throws Exception */ public function itShouldFailToValidateInvalidRequests($tamperedKey, $tamperedValue, $expectedErrorMessage, $expectedErrorCode) { @@ -118,6 +130,7 @@ public function requestTamperingProvider() /** * @test + * @throws Exception */ public function itShouldValidateRequestUsingQueryString() { @@ -137,6 +150,7 @@ public function itShouldValidateRequestUsingQueryString() /** * @test + * @throws Exception */ public function itShouldValidatePresignedUrlRequestWithSpecialCharacters() { @@ -151,7 +165,7 @@ public function itShouldValidatePresignedUrlRequestWithSpecialCharacters() 'SERVER_NAME' => 'service.example.com', ); $keyDB = array('service_api_key' => 'service_secret'); - $this->createEscher('eu/service/ems_request', new \DateTime('20150310T173248Z', new \DateTimeZone('GMT')))->authenticate($keyDB, $serverVars); + $this->createEscher('eu/service/ems_request')->authenticate($keyDB, $serverVars); } /** @@ -179,6 +193,7 @@ public function itShouldFailToValidateInvalidQueryStrings() /** * @test + * @throws Exception */ public function itShouldValidatePresignedUrlRequestWithUnindexedArray() { @@ -193,11 +208,12 @@ public function itShouldValidatePresignedUrlRequestWithUnindexedArray() 'SERVER_NAME' => 'service.example.com', ); $keyDB = array('service_api_key' => 'service_secret'); - $this->createEscher('eu/service/ems_request', new \DateTime('20150310T173248Z', new \DateTimeZone('GMT')))->authenticate($keyDB, $serverVars); + $this->createEscher('eu/service/ems_request')->authenticate($keyDB, $serverVars); } /** * @test + * @throws Exception */ public function itShouldValidatePresignedUrlRequestWithIndexedArray() { @@ -212,11 +228,12 @@ public function itShouldValidatePresignedUrlRequestWithIndexedArray() 'SERVER_NAME' => 'service.example.com', ); $keyDB = array('service_api_key' => 'service_secret'); - $this->createEscher('eu/service/ems_request', new \DateTime('20150310T173248Z', new \DateTimeZone('GMT')))->authenticate($keyDB, $serverVars); + $this->createEscher('eu/service/ems_request')->authenticate($keyDB, $serverVars); } /** * @test + * @throws Exception */ public function itShouldValidatePresignedUrlIfSignatureIsTheFirstParam() { @@ -231,11 +248,12 @@ public function itShouldValidatePresignedUrlIfSignatureIsTheFirstParam() 'SERVER_NAME' => 'service.example.com', ); $keyDB = array('service_api_key' => 'service_secret'); - $this->createEscher('eu/service/ems_request', new \DateTime('20150310T173248Z', new \DateTimeZone('GMT')))->authenticate($keyDB, $serverVars); + $this->createEscher('eu/service/ems_request')->authenticate($keyDB, $serverVars); } /** * @test + * @throws Exception */ public function itShouldValidatePresignedUrlIfSignatureIsInTheMiddleOfTheQueryString() { @@ -250,9 +268,14 @@ public function itShouldValidatePresignedUrlIfSignatureIsInTheMiddleOfTheQuerySt 'SERVER_NAME' => 'service.example.com', ); $keyDB = array('service_api_key' => 'service_secret'); - $this->createEscher('eu/service/ems_request', new \DateTime('20150310T173248Z', new \DateTimeZone('GMT')))->authenticate($keyDB, $serverVars); + $this->createEscher('eu/service/ems_request')->authenticate($keyDB, $serverVars); } + /** + * @param $dateString + * @return string + * @throws Exception + */ private function strtotime($dateString) { return Utils::parseLongDate($dateString)->format('U'); diff --git a/test/unit/SignRequestUsingHeaderTest.php b/test/unit/SignRequestUsingHeaderTest.php index 79a55a0..6874f78 100644 --- a/test/unit/SignRequestUsingHeaderTest.php +++ b/test/unit/SignRequestUsingHeaderTest.php @@ -21,7 +21,7 @@ public function itShouldSignRequest() $headersToSign = array('content-type', 'host', 'x-ems-date'); $actualHeaders = $this->createEscher('us-east-1/iam/aws4_request')->signRequest( 'AKIDEXAMPLE', 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY', - 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign + 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign, $this->getDate() ); $this->assertEqualMaps($expectedHeaders, $actualHeaders); } @@ -47,7 +47,7 @@ public function itShouldSignRequestWithUppercaseHeader() $headersToSign = array('content-type', 'host', 'x-ems-date', 'TEST'); $actualHeaders = $this->createEscher('us-east-1/iam/aws4_request')->signRequest( 'AKIDEXAMPLE', 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY', - 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign + 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign, $this->getDate() ); $this->assertEqualMaps($expectedHeaders, $actualHeaders); } @@ -70,7 +70,7 @@ public function itShouldAutomagicallyAddHostHeader() $headersToSign = array('content-type', 'host', 'x-ems-date'); $actualHeaders = $this->createEscher('us-east-1/iam/aws4_request')->signRequest( 'AKIDEXAMPLE', 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY', - 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign + 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign, $this->getDate() ); $this->assertEqualMaps($expectedHeaders, $actualHeaders); } @@ -88,7 +88,7 @@ public function itShouldAutomagicallyAddHostHeaderWithPort($url, $expectedHost) $headersToSign = array('content-type', 'host', 'x-ems-date'); $actualHeaders = $this->createEscher('us-east-1/iam/aws4_request')->signRequest( 'AKIDEXAMPLE', 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY', - 'POST', $url, 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign + 'POST', $url, 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign, $this->getDate() ); $this->assertEquals($expectedHost, $actualHeaders['host']); } @@ -125,7 +125,7 @@ public function itShouldAutomagicallyAddDateAndHostToSignedHeaders() $headersToSign = array('content-type'); $actualHeaders = $this->createEscher('us-east-1/iam/aws4_request')->signRequest( 'AKIDEXAMPLE', 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY', - 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign + 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign, $this->getDate() ); $this->assertEqualMaps($expectedHeaders, $actualHeaders); } @@ -151,7 +151,7 @@ public function itShouldOnlySignHeadersExplicitlySetToBeSigned() $headersToSign = array('content-type', 'host', 'x-ems-date'); $actualHeaders = $this->createEscher('us-east-1/iam/aws4_request')->signRequest( 'AKIDEXAMPLE', 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY', - 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign + 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign, $this->getDate() ); $this->assertEqualMaps($expectedHeaders, $actualHeaders); } @@ -175,7 +175,7 @@ public function itShouldUseTheProvidedAuthHeaderName() $headersToSign = array('content-type', 'host', 'x-ems-date'); $actualHeaders = $this->createEscher('us-east-1/iam/aws4_request')->setAuthHeaderKey('Custom-Auth-Header')->signRequest( 'AKIDEXAMPLE', 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY', - 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign + 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign, $this->getDate() ); $this->assertEqualMaps($expectedHeaders, $actualHeaders); } @@ -200,7 +200,7 @@ public function itShouldUseTheProvidedAlgoPrefix() $headersToSign = array('content-type', 'host', 'x-ems-date'); $actualHeaders = $escher->signRequest( 'AKIDEXAMPLE', 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY', - 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign + 'POST', 'http://iam.amazonaws.com/', 'Action=ListUsers&Version=2010-05-08', $inputHeaders, $headersToSign, $this->getDate() ); $this->assertEqualMaps($expectedHeaders, $actualHeaders); } @@ -216,11 +216,11 @@ public function itShouldGenerateSignedHeaders() ); $date = new DateTime('2011/05/11 12:00:00', new DateTimeZone("UTC")); - $escher = $this->createEscher('us-east-1/host/aws4_request', $date); + $escher = $this->createEscher('us-east-1/host/aws4_request'); $actualHeaders = $escher->signRequest( 'th3K3y', 'very_secure', - 'GET', 'http://example.com/something', '', $inputHeaders, array() + 'GET', 'http://example.com/something', '', $inputHeaders, array(), $date ); $expectedHeaders = array( diff --git a/test/unit/SignRequestUsingQueryStringTest.php b/test/unit/SignRequestUsingQueryStringTest.php index a0f26db..0718686 100644 --- a/test/unit/SignRequestUsingQueryStringTest.php +++ b/test/unit/SignRequestUsingQueryStringTest.php @@ -9,7 +9,7 @@ class SignRequestUsingQueryStringTest extends TestBase */ public function itShouldGenerateSignedUrl() { - $signedUrl = $this->createEscher()->presignUrl('th3K3y', 'very_secure', 'http://example.com/something?foo=bar&baz=barbaz', $this->expires); + $signedUrl = $this->createEscher()->presignUrl('th3K3y', 'very_secure', 'http://example.com/something?foo=bar&baz=barbaz', $this->expires, $this->getDate()); $expectedSignedUrl = 'http://example.com/something?foo=bar&baz=barbaz&X-EMS-Algorithm=EMS-HMAC-SHA256&X-EMS-Credentials=th3K3y%2F20110511%2Fus-east-1%2Fhost%2Faws4_request&X-EMS-Date=20110511T120000Z&X-EMS-Expires=123456&X-EMS-SignedHeaders=host&X-EMS-Signature=fbc9dbb91670e84d04ad2ae7505f4f52ab3ff9e192b8233feeae57e9022c2b67'; @@ -21,7 +21,7 @@ public function itShouldGenerateSignedUrl() */ public function itShouldHandlePort() { - $signedUrl = $this->createEscher()->presignUrl('th3K3y', 'very_secure', 'http://example.com:5000/something?foo=bar&baz=barbaz', $this->expires); + $signedUrl = $this->createEscher()->presignUrl('th3K3y', 'very_secure', 'http://example.com:5000/something?foo=bar&baz=barbaz', $this->expires, $this->getDate()); $expectedSignedUrl = 'http://example.com:5000/something?foo=bar&baz=barbaz&X-EMS-Algorithm=EMS-HMAC-SHA256&X-EMS-Credentials=th3K3y%2F20110511%2Fus-east-1%2Fhost%2Faws4_request&X-EMS-Date=20110511T120000Z&X-EMS-Expires=123456&X-EMS-SignedHeaders=host&X-EMS-Signature=7f7032b393945a0167fe65d35a7e2827a781ecab9019d814adf95c23bfa5e458'; @@ -33,7 +33,7 @@ public function itShouldHandlePort() */ public function itShouldRespectWhenUrlHasLocationHash() { - $signedUrl = $this->createEscher()->presignUrl('th3K3y', 'very_secure', 'http://example.com:5000/something?foo=bar&baz=barbaz#/client_fragment', $this->expires); + $signedUrl = $this->createEscher()->presignUrl('th3K3y', 'very_secure', 'http://example.com:5000/something?foo=bar&baz=barbaz#/client_fragment', $this->expires, $this->getDate()); $expectedSignedUrl = 'http://example.com:5000/something?foo=bar&baz=barbaz&X-EMS-Algorithm=EMS-HMAC-SHA256&X-EMS-Credentials=th3K3y%2F20110511%2Fus-east-1%2Fhost%2Faws4_request&X-EMS-Date=20110511T120000Z&X-EMS-Expires=123456&X-EMS-SignedHeaders=host&X-EMS-Signature=7f7032b393945a0167fe65d35a7e2827a781ecab9019d814adf95c23bfa5e458#/client_fragment'; @@ -45,10 +45,13 @@ public function itShouldRespectWhenUrlHasLocationHash() */ public function itShouldRespectWhenUrlHasSpecialChars() { - $signedUrl = $this->createEscher('eu/service/ems_request', new DateTime('20150310T173248Z', new DateTimeZone('GMT')))->presignUrl( + $date = new DateTime('20150310T173248Z', new DateTimeZone('GMT')); + $signedUrl = $this->createEscher('eu/service/ems_request')->presignUrl( 'service_api_key', 'service_secret', - 'https://service.example.com/login?id=12345678&domain=login.example.com&redirect_to=https%3A%2F%2Fhome.dev%2Fbootstrap.php%3Fr%3Dservice%2Findex%26service%3Dservice_name%3F' + 'https://service.example.com/login?id=12345678&domain=login.example.com&redirect_to=https%3A%2F%2Fhome.dev%2Fbootstrap.php%3Fr%3Dservice%2Findex%26service%3Dservice_name%3F', + \Escher\Escher::DEFAULT_EXPIRES, + $date ); $expectedSignedUrl = 'https://service.example.com/login?id=12345678&domain=login.example.com&redirect_to=https%3A%2F%2Fhome.dev%2Fbootstrap.php%3Fr%3Dservice%2Findex%26service%3Dservice_name%3F&X-EMS-Algorithm=EMS-HMAC-SHA256&X-EMS-Credentials=service_api_key%2F20150310%2Feu%2Fservice%2Fems_request&X-EMS-Date=20150310T173248Z&X-EMS-Expires=86400&X-EMS-SignedHeaders=host&X-EMS-Signature=661f2147c77b6784be5a60a8b842a96de6327653f1ed5d4305da43103c69a6f5'; diff --git a/test/unit/TestBase.php b/test/unit/TestBase.php index 80a2678..862709b 100644 --- a/test/unit/TestBase.php +++ b/test/unit/TestBase.php @@ -13,16 +13,11 @@ protected function assertEqualMaps(array $expected, array $actual, $message = '' /** * @param string $credentialScope - * @param DateTime $date * @return Escher */ - protected function createEscher($credentialScope = 'us-east-1/host/aws4_request', $date = null) + protected function createEscher($credentialScope = 'us-east-1/host/aws4_request') { - if (is_null($date)) - { - $date = $this->getDate(); - } - return Escher::create($credentialScope, $date) + return Escher::create($credentialScope) ->setAlgoPrefix('EMS')->setVendorKey('EMS')->setAuthHeaderKey('X-Ems-Auth')->setDateHeaderKey('X-Ems-Date'); }