<?php
/**
 * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
namespace Amazon\InstantAccess\Signature;

use Amazon\InstantAccess\Utils\HttpUtils;
use Amazon\InstantAccess\Utils\IOUtils;
use Amazon\InstantAccess\Utils\DateUtils;

class SignerTest extends \PHPUnit_Framework_TestCase
{
    public function testGetAuthorizationHeader()
    {
        $dateNow = \DateTime::createFromFormat(DateUtils::DATE_FORMAT_ISO8601, '20110909T233600Z');

        $shortDate = $dateNow->format(DateUtils::DATE_FORMAT_SHORT);
        $isoDate = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601);

        $server = array();
        $server['HTTP_HOST'] = 'amazon.com';
        $server['SERVER_PROTOCOL'] = 'HTTP/1.1';
        $server['SERVER_PORT'] = '80';
        $server['REQUEST_URI'] = '/';
        $server['REQUEST_METHOD'] = 'GET';
        $server['CONTENT_TYPE'] = 'application/json';
        $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $isoDate;

        $content = 'body';

        $request = new Request($server, $content);

        $credential = new Credential('SECRETKEY', 'KEYID');
        $store = new CredentialStore();
        $store->add($credential);

        $signer = new Signer();

        // remove the host header because this signature was generated by the Java SDK
        // and it does not automatically adds this header
        $headers = &$request->getHeaders();
        unset($headers['host']);

        $header = $signer->getAuthorizationHeader($request, $credential, $shortDate, $isoDate);

        $this->assertEquals('DTA1-HMAC-SHA256 SignedHeaders=content-type;x-amz-date, Credential=KEYID/20110909, Signature=4d2f81ea2cf8d6963f8176a22eec4c65ae95c63502326a7c148686da7d50f47e', $header);
    }

    public function testSignAndVerify()
    {
        $server = array();
        $server['HTTP_HOST'] = 'amazon.com';
        $server['SERVER_PROTOCOL'] = 'HTTP/1.1';
        $server['SERVER_PORT'] = '80';
        $server['REQUEST_URI'] = '/service/foo.php';
        $server['REQUEST_METHOD'] = 'POST';
        $server['HTTP_ACCEPT'] = '*/*';
        $server['CONTENT_TYPE'] = 'application/json';

        $content = '{"operation" : "bar"}';

        $request = new Request($server, $content);

        $credential = new Credential('SECRET', 'PUBLIC');
        $store = new CredentialStore();
        $store->add($credential);

        $signer = new Signer();

        $signer->sign($request, $credential);

        $verified = $signer->verify($request, $store);

        $this->assertTrue($verified);
    }

    public function testVerifyEmptyCredentialStore()
    {
        $this->setExpectedException('InvalidArgumentException');

        $server = array();
        $server['HTTP_HOST'] = 'amazon.com';
        $server['SERVER_PROTOCOL'] = 'HTTP/1.1';
        $server['SERVER_PORT'] = '80';
        $server['REQUEST_URI'] = '/service/foo.php';
        $server['REQUEST_METHOD'] = 'POST';
        $server['HTTP_ACCEPT'] = '*/*';
        $server['CONTENT_TYPE'] = 'application/json';

        $content = '{"operation" : "bar"}';

        $request = new Request($server, $content);

        $signer = new Signer();
        $signer->verify($request, new CredentialStore());
    }

    public function testVerifyRequestNoDate()
    {
        $server = array();
        $server['HTTP_HOST'] = 'amazon.com';
        $server['SERVER_PROTOCOL'] = 'HTTP/1.1';
        $server['SERVER_PORT'] = '80';
        $server['REQUEST_URI'] = '/';
        $server['REQUEST_METHOD'] = 'GET';
        $server['CONTENT_TYPE'] = 'application/json';

        $content = 'body';

        $request = new Request($server, $content);

        $credential = new Credential('SECRETKEY', 'KEYID');
        $store = new CredentialStore();
        $store->add($credential);

        $signer = new Signer();

        $this->assertFalse($signer->verify($request, $store));
    }

    public function testVerifyRequestNoAuthorization()
    {
        $server = array();
        $server['HTTP_HOST'] = 'amazon.com';
        $server['SERVER_PROTOCOL'] = 'HTTP/1.1';
        $server['SERVER_PORT'] = '80';
        $server['REQUEST_URI'] = '/';
        $server['REQUEST_METHOD'] = 'GET';
        $server['CONTENT_TYPE'] = 'application/json';

        $dateNow = new \DateTime('@' . time());
        $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601);

        $content = 'body';

        $request = new Request($server, $content);

        $credential = new Credential('SECRETKEY', 'KEYID');
        $store = new CredentialStore();
        $store->add($credential);

        $signer = new Signer();

        $this->assertFalse($signer->verify($request, $store));
    }

    public function testVerifyRequestWithInvalidAuthorization()
    {
        $server = array();
        $server['HTTP_HOST'] = 'amazon.com';
        $server['SERVER_PROTOCOL'] = 'HTTP/1.1';
        $server['SERVER_PORT'] = '80';
        $server['REQUEST_URI'] = '/';
        $server['REQUEST_METHOD'] = 'GET';
        $server['CONTENT_TYPE'] = 'application/json';

        $dateNow = new \DateTime('@' . time());
        $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601);

        $server['HTTP_' . HttpUtils::AUTHORIZATION_HEADER] = 'foo';

        $content = 'body';

        $request = new Request($server, $content);

        $credential = new Credential('SECRETKEY', 'KEYID');
        $store = new CredentialStore();
        $store->add($credential);

        $signer = new Signer();

        $this->assertFalse($signer->verify($request, $store));
    }

    public function testVerifyRequestWithLateMessage()
    {
        $server = array();
        $server['HTTP_HOST'] = 'amazon.com';
        $server['SERVER_PROTOCOL'] = 'HTTP/1.1';
        $server['SERVER_PORT'] = '80';
        $server['REQUEST_URI'] = '/';
        $server['REQUEST_METHOD'] = 'GET';
        $server['CONTENT_TYPE'] = 'application/json';

        $dateOld = \DateTime::createFromFormat(DateUtils::DATE_FORMAT_ISO8601, '20110909T233600Z');
        $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $dateOld->format(DateUtils::DATE_FORMAT_ISO8601);

        $server['HTTP_' . HttpUtils::AUTHORIZATION_HEADER] = 'DTA1-HMAC-SHA256 SignedHeaders=xxx, Credential=KEYID/20110909, Signature=xxx';

        $content = 'body';

        $request = new Request($server, $content);

        $credential = new Credential('SECRETKEY', 'KEYID');
        $store = new CredentialStore();
        $store->add($credential);

        $signer = new Signer();

        $this->assertFalse($signer->verify($request, $store));
    }

    public function testVerifyRequestWithInvalidCredential()
    {
        $server = array();
        $server['HTTP_HOST'] = 'amazon.com';
        $server['SERVER_PROTOCOL'] = 'HTTP/1.1';
        $server['SERVER_PORT'] = '80';
        $server['REQUEST_URI'] = '/';
        $server['REQUEST_METHOD'] = 'GET';
        $server['CONTENT_TYPE'] = 'application/json';

        $dateNow = new \DateTime('@' . time());
        $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601);

        $server['HTTP_' . HttpUtils::AUTHORIZATION_HEADER] = 'DTA1-HMAC-SHA256 SignedHeaders=xxx, Credential=NOTKEYID/' . $dateNow->format(DateUtils::DATE_FORMAT_SHORT) . ', Signature=xxx';

        $content = 'body';

        $request = new Request($server, $content);

        $credential = new Credential('SECRETKEY', 'KEYID');
        $store = new CredentialStore();
        $store->add($credential);

        $signer = new Signer();

        $this->assertFalse($signer->verify($request, $store));
    }

    public function testVerifyRequestWithInvalidCredentialDate()
    {
        $server = array();
        $server['HTTP_HOST'] = 'amazon.com';
        $server['SERVER_PROTOCOL'] = 'HTTP/1.1';
        $server['SERVER_PORT'] = '80';
        $server['REQUEST_URI'] = '/';
        $server['REQUEST_METHOD'] = 'GET';
        $server['CONTENT_TYPE'] = 'application/json';

        $dateNow = new \DateTime('@' . time());
        $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601);

        $server['HTTP_' . HttpUtils::AUTHORIZATION_HEADER] = 'DTA1-HMAC-SHA256 SignedHeaders=xxx, Credential=KEYID/20110909, Signature=xxx';

        $content = 'body';

        $request = new Request($server, $content);

        $credential = new Credential('SECRETKEY', 'KEYID');
        $store = new CredentialStore();
        $store->add($credential);

        $signer = new Signer();

        $this->assertFalse($signer->verify($request, $store));
    }

    public function testVerifyRequestWithInvalidSignature()
    {
        $server = array();
        $server['HTTP_HOST'] = 'amazon.com';
        $server['SERVER_PROTOCOL'] = 'HTTP/1.1';
        $server['SERVER_PORT'] = '80';
        $server['REQUEST_URI'] = '/';
        $server['REQUEST_METHOD'] = 'GET';
        $server['CONTENT_TYPE'] = 'application/json';

        $dateNow = new \DateTime('@' . time());
        $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601);

        $server['HTTP_' . HttpUtils::AUTHORIZATION_HEADER] = 'DTA1-HMAC-SHA256 SignedHeaders=xxx, Credential=KEYID/' . $dateNow->format(DateUtils::DATE_FORMAT_SHORT) . ', Signature=xxx';

        $content = 'body';

        $request = new Request($server, $content);

        $credential = new Credential('SECRETKEY', 'KEYID');
        $store = new CredentialStore();
        $store->add($credential);

        $signer = new Signer();

        $this->assertFalse($signer->verify($request, $store));
    }

    public function testGetAuthorizationHeaderInvalidDates()
    {
        $this->setExpectedException('InvalidArgumentException');

        $server = array();
        $server['HTTP_HOST'] = 'amazon.com';
        $server['SERVER_PROTOCOL'] = 'HTTP/1.1';
        $server['SERVER_PORT'] = '80';
        $server['REQUEST_URI'] = '/service/foo.php';
        $server['REQUEST_METHOD'] = 'POST';
        $server['HTTP_ACCEPT'] = '*/*';
        $server['CONTENT_TYPE'] = 'application/json';

        $content = '{"operation" : "bar"}';

        $request = new Request($server, $content);

        $signer = new Signer();
        $header = $signer->getAuthorizationHeader($request, new Credential('SECRET', 'PUBLIC'), null, null);
    }
}
