From e5822f5f8d5e41781ae65003b3df93e0b9a0e2ab Mon Sep 17 00:00:00 2001 From: Julien Loizelet Date: Thu, 5 Sep 2024 11:36:01 +0900 Subject: [PATCH] feat(app sec): Add AppSecRequest class --- src/Client/AbstractClient.php | 48 ++++++++--- src/Client/HttpMessage/AppSecRequest.php | 42 ++++++++++ src/Client/HttpMessage/Request.php | 5 +- src/Client/RequestHandler/Curl.php | 64 ++++++++++----- src/Client/RequestHandler/FileGetContents.php | 37 ++++++--- src/Constants.php | 4 + tests/MockedData.php | 4 + tests/Unit/AbstractClientTest.php | 59 ++++++++++++-- tests/Unit/CurlTest.php | 79 ++++++++++++++++++- tests/Unit/FileGetContentsTest.php | 75 ++++++++++++++++++ tests/Unit/RequestTest.php | 28 ++++--- 11 files changed, 385 insertions(+), 60 deletions(-) create mode 100644 src/Client/HttpMessage/AppSecRequest.php diff --git a/src/Client/AbstractClient.php b/src/Client/AbstractClient.php index ea282b4..24db3e0 100644 --- a/src/Client/AbstractClient.php +++ b/src/Client/AbstractClient.php @@ -4,11 +4,11 @@ namespace CrowdSec\Common\Client; +use CrowdSec\Common\Client\HttpMessage\AppSecRequest; use CrowdSec\Common\Client\HttpMessage\Request; use CrowdSec\Common\Client\HttpMessage\Response; use CrowdSec\Common\Client\RequestHandler\Curl; use CrowdSec\Common\Client\RequestHandler\RequestHandlerInterface; -use CrowdSec\Common\Constants; use Monolog\Handler\NullHandler; use Monolog\Logger; use Psr\Log\LoggerInterface; @@ -33,6 +33,10 @@ abstract class AbstractClient * @var string[] */ private $allowedMethods = ['POST', 'GET', 'DELETE']; + /** + * @var string[] + */ + private $allowedAppSecMethods = ['POST', 'GET']; /** * @var LoggerInterface */ @@ -53,7 +57,7 @@ abstract class AbstractClient public function __construct( array $configs, ?RequestHandlerInterface $requestHandler = null, - ?LoggerInterface $logger = null + ?LoggerInterface $logger = null, ) { $this->configs = $configs; $this->requestHandler = ($requestHandler) ?: new Curl($this->configs); @@ -88,11 +92,14 @@ public function getRequestHandler(): RequestHandlerInterface return $this->requestHandler; } - public function getUrl(string $type = Constants::TYPE_REST): string + public function getUrl(): string { - $url = Constants::TYPE_APPSEC === $type ? $this->appSecUrl : $this->url; + return rtrim($this->url, '/') . '/'; + } - return rtrim($url, '/') . '/'; + public function getAppSecUrl(): string + { + return rtrim($this->appSecUrl, '/') . '/'; } /** @@ -105,7 +112,6 @@ protected function request( string $endpoint, array $parameters = [], array $headers = [], - string $type = Constants::TYPE_REST ): array { $method = strtoupper($method); if (!in_array($method, $this->allowedMethods)) { @@ -115,7 +121,31 @@ protected function request( } $response = $this->sendRequest( - new Request($this->getFullUrl($endpoint, $type), $method, $headers, $parameters) + new Request($this->getFullUrl($endpoint), $method, $headers, $parameters) + ); + + return $this->formatResponseBody($response); + } + + /** + * Performs an HTTP request (POST, GET) to AppSec and returns its response body as an array. + * + * @throws ClientException + */ + protected function requestAppSec( + string $method, + array $headers = [], + string $rawBody = '', + ): array { + $method = strtoupper($method); + if (!in_array($method, $this->allowedAppSecMethods)) { + $message = "Method ($method) is not allowed."; + $this->logger->error($message, ['type' => 'CLIENT_APPSEC_REQUEST']); + throw new ClientException($message); + } + + $response = $this->sendRequest( + new AppSecRequest($this->getAppSecUrl(), $method, $headers, $rawBody) ); return $this->formatResponseBody($response); @@ -160,8 +190,8 @@ private function formatResponseBody(Response $response): array return $decoded; } - private function getFullUrl(string $endpoint, string $type = Constants::TYPE_REST): string + private function getFullUrl(string $endpoint): string { - return $this->getUrl($type) . ltrim($endpoint, '/'); + return $this->getUrl() . ltrim($endpoint, '/'); } } diff --git a/src/Client/HttpMessage/AppSecRequest.php b/src/Client/HttpMessage/AppSecRequest.php new file mode 100644 index 0000000..b999e4d --- /dev/null +++ b/src/Client/HttpMessage/AppSecRequest.php @@ -0,0 +1,42 @@ +rawBody = $rawBody; + parent::__construct($uri, $method, $headers); + } + + public function getRawBody(): string + { + return $this->rawBody; + } +} diff --git a/src/Client/HttpMessage/Request.php b/src/Client/HttpMessage/Request.php index b71f949..994e21d 100644 --- a/src/Client/HttpMessage/Request.php +++ b/src/Client/HttpMessage/Request.php @@ -4,8 +4,6 @@ namespace CrowdSec\Common\Client\HttpMessage; -use CrowdSec\Common\Constants; - /** * Request that will be sent to CrowdSec. * @@ -43,11 +41,10 @@ public function __construct( string $method, array $headers = [], array $parameters = [], - string $type = Constants::TYPE_REST ) { $this->uri = $uri; $this->method = $method; - $this->headers = Constants::TYPE_REST === $type ? array_merge($this->headers, $headers) : $headers; + $this->headers = array_merge($this->headers, $headers); $this->parameters = $parameters; } diff --git a/src/Client/RequestHandler/Curl.php b/src/Client/RequestHandler/Curl.php index e6160ee..a8e7f38 100644 --- a/src/Client/RequestHandler/Curl.php +++ b/src/Client/RequestHandler/Curl.php @@ -5,6 +5,7 @@ namespace CrowdSec\Common\Client\RequestHandler; use CrowdSec\Common\Client\ClientException; +use CrowdSec\Common\Client\HttpMessage\AppSecRequest; use CrowdSec\Common\Client\HttpMessage\Request; use CrowdSec\Common\Client\HttpMessage\Response; use CrowdSec\Common\Constants; @@ -62,22 +63,9 @@ protected function getResponseHttpCode($handle) return curl_getinfo($handle, \CURLINFO_HTTP_CODE); } - private function handleConfigs(): array + private function handleTimeout(): array { - $result = [\CURLOPT_SSL_VERIFYPEER => false]; - $authType = $this->getConfig('auth_type'); - if ($authType && Constants::AUTH_TLS === $authType) { - $verifyPeer = $this->getConfig('tls_verify_peer') ?? true; - $result[\CURLOPT_SSL_VERIFYPEER] = $verifyPeer; - // The --cert option - $result[\CURLOPT_SSLCERT] = $this->getConfig('tls_cert_path') ?? ''; - // The --key option - $result[\CURLOPT_SSLKEY] = $this->getConfig('tls_key_path') ?? ''; - if ($verifyPeer) { - // The --cacert option - $result[\CURLOPT_CAINFO] = $this->getConfig('tls_ca_cert_path') ?? ''; - } - } + $result = []; $timeout = $this->getConfig('api_timeout') ?? Constants::API_TIMEOUT; /** * To obtain an unlimited timeout, we don't pass the option (as it is the default behavior). @@ -100,13 +88,38 @@ private function handleConfigs(): array return $result; } - private function handleMethod(string $method, string $url, array $parameters = []): array + private function handleSSL(Request $request): array + { + $result = [\CURLOPT_SSL_VERIFYPEER => false]; + if ($request instanceof AppSecRequest) { + // AppSec does not require SSL verification + return $result; + } + + $authType = $this->getConfig('auth_type'); + if ($authType && Constants::AUTH_TLS === $authType) { + $verifyPeer = $this->getConfig('tls_verify_peer') ?? true; + $result[\CURLOPT_SSL_VERIFYPEER] = $verifyPeer; + // The --cert option + $result[\CURLOPT_SSLCERT] = $this->getConfig('tls_cert_path') ?? ''; + // The --key option + $result[\CURLOPT_SSLKEY] = $this->getConfig('tls_key_path') ?? ''; + if ($verifyPeer) { + // The --cacert option + $result[\CURLOPT_CAINFO] = $this->getConfig('tls_ca_cert_path') ?? ''; + } + } + + return $result; + } + + private function handleMethod(string $method, string $url, array $parameters = [], $rawBody = ''): array { $result = []; if ('POST' === strtoupper($method)) { $result[\CURLOPT_POST] = true; $result[\CURLOPT_CUSTOMREQUEST] = 'POST'; - $result[\CURLOPT_POSTFIELDS] = json_encode($parameters); + $result[\CURLOPT_POSTFIELDS] = $rawBody ?: json_encode($parameters); } elseif ('GET' === strtoupper($method)) { $result[\CURLOPT_POST] = false; $result[\CURLOPT_CUSTOMREQUEST] = 'GET'; @@ -132,19 +145,27 @@ private function handleMethod(string $method, string $url, array $parameters = [ */ private function createOptions(Request $request): array { + $isAppSec = $request instanceof AppSecRequest; $headers = $request->getHeaders(); $method = $request->getMethod(); $url = $request->getUri(); $parameters = $request->getParams(); - if (!isset($headers['User-Agent'])) { + if (!isset($headers['User-Agent']) && !$isAppSec) { throw new ClientException('User agent is required', 400); } + $rawBody = ''; + if ($isAppSec) { + /** @var AppSecRequest $request */ + $rawBody = $request->getRawBody(); + } $options = [ \CURLOPT_HEADER => false, \CURLOPT_RETURNTRANSFER => true, - \CURLOPT_USERAGENT => $headers['User-Agent'], \CURLOPT_ENCODING => '', ]; + if (isset($headers['User-Agent'])) { + $options[\CURLOPT_USERAGENT] = $headers['User-Agent']; + } $options[\CURLOPT_HTTPHEADER] = []; foreach ($headers as $key => $values) { @@ -153,8 +174,9 @@ private function createOptions(Request $request): array } } // We need to keep keys indexes (array_merge not keeping indexes) - $options += $this->handleConfigs(); - $options += $this->handleMethod($method, $url, $parameters); + $options += $this->handleSSL($request); + $options += $this->handleTimeout(); + $options += $this->handleMethod($method, $url, $parameters, $rawBody); return $options; } diff --git a/src/Client/RequestHandler/FileGetContents.php b/src/Client/RequestHandler/FileGetContents.php index 6f10c80..24fc7b7 100644 --- a/src/Client/RequestHandler/FileGetContents.php +++ b/src/Client/RequestHandler/FileGetContents.php @@ -5,6 +5,7 @@ namespace CrowdSec\Common\Client\RequestHandler; use CrowdSec\Common\Client\ClientException; +use CrowdSec\Common\Client\HttpMessage\AppSecRequest; use CrowdSec\Common\Client\HttpMessage\Request; use CrowdSec\Common\Client\HttpMessage\Response; use CrowdSec\Common\Constants; @@ -95,9 +96,15 @@ protected function convertHeadersToString(array $headers): string private function createContextConfig(Request $request): array { $headers = $request->getHeaders(); - if (!isset($headers['User-Agent'])) { + $isAppSec = $request instanceof AppSecRequest; + if (!isset($headers['User-Agent']) && !$isAppSec) { throw new ClientException('User agent is required', 400); } + $rawBody = ''; + if ($isAppSec) { + /** @var AppSecRequest $request */ + $rawBody = $request->getRawBody(); + } $header = $this->convertHeadersToString($headers); $method = $request->getMethod(); $timeout = $this->getConfig('api_timeout'); @@ -112,24 +119,36 @@ private function createContextConfig(Request $request): array ], ]; - $config['ssl'] = ['verify_peer' => false]; + $config += $this->handleSSL($request); + + if ('POST' === strtoupper($method)) { + $config['http']['content'] = + $isAppSec ? $rawBody : json_encode($request->getParams()); + } + + return $config; + } + + private function handleSSL(Request $request): array + { + $result = ['ssl' => ['verify_peer' => false]]; + if ($request instanceof AppSecRequest) { + // AppSec does not require SSL verification + return $result; + } $authType = $this->getConfig('auth_type'); if ($authType && Constants::AUTH_TLS === $authType) { $verifyPeer = $this->getConfig('tls_verify_peer') ?? true; - $config['ssl'] = [ + $result['ssl'] = [ 'verify_peer' => $verifyPeer, 'local_cert' => $this->getConfig('tls_cert_path') ?? '', 'local_pk' => $this->getConfig('tls_key_path') ?? '', ]; if ($verifyPeer) { - $config['ssl']['cafile'] = $this->getConfig('tls_ca_cert_path') ?? ''; + $result['ssl']['cafile'] = $this->getConfig('tls_ca_cert_path') ?? ''; } } - if ('POST' === strtoupper($method)) { - $config['http']['content'] = json_encode($request->getParams()); - } - - return $config; + return $result; } } diff --git a/src/Constants.php b/src/Constants.php index ae06ccc..34f9e5d 100644 --- a/src/Constants.php +++ b/src/Constants.php @@ -56,6 +56,10 @@ class Constants * @var string The LISTS origin for decisions */ public const ORIGIN_LISTS = 'lists'; + /** + * @var string The CrowdSec App Sec raw body param + */ + public const APPSEC_RAW_BODY_PARAM = 'app_sec_raw_body'; /** * @var string The ban remediation */ diff --git a/tests/MockedData.php b/tests/MockedData.php index a8569ef..a768368 100644 --- a/tests/MockedData.php +++ b/tests/MockedData.php @@ -32,5 +32,9 @@ class MockedData public const UNAUTHORIZED = <<assertEquals( - Constants::APPSEC_URL . '/test-endpoint', - $fullUrl, - 'Full Url should be ok for AppSec' + Constants::APPSEC_URL . '/', + $appSecUrl, + 'App Sec Url should be ok' ); // formatResponseBody $jsonBody = json_encode(['message' => 'ok']); @@ -331,4 +334,48 @@ public function testSendRequest() 'Not allowed method should throw error' ); } + + public function testSendAppSecRequest() + { + $configs = $this->configs; + $requestHandler = $this->getCurlMock(['handle']); + + $client = $this->getMockForAbstractClass(AbstractClient::class, [$configs, $requestHandler]); + + $response = new Response(MockedData::APPSEC_ALLOWED, 200); + + $requestHandler->method('handle')->willReturn( + $response + ); + + $decodedResponse = PHPUnitUtil::callMethod( + $client, + 'requestAppSec', + ['POST', ['test' => 'test'], 'this is a raw body'] + ); + + $this->assertEquals( + json_decode(MockedData::APPSEC_ALLOWED, true), + $decodedResponse, + 'Decoded response should be correct' + ); + + $error = false; + try { + PHPUnitUtil::callMethod( + $client, + 'requestAppSec', + ['PUT', ['test' => 'test'], 'this is a raw body'] + ); + } catch (ClientException $e) { + $error = $e->getMessage(); + } + + PHPUnitUtil::assertRegExp( + $this, + '/Method \(PUT\) is not allowed/', + $error, + 'Not allowed method should throw error' + ); + } } diff --git a/tests/Unit/CurlTest.php b/tests/Unit/CurlTest.php index a8e64ee..858fe13 100644 --- a/tests/Unit/CurlTest.php +++ b/tests/Unit/CurlTest.php @@ -16,6 +16,7 @@ */ use CrowdSec\Common\Client\ClientException; +use CrowdSec\Common\Client\HttpMessage\AppSecRequest; use CrowdSec\Common\Client\HttpMessage\Request; use CrowdSec\Common\Client\HttpMessage\Response; use CrowdSec\Common\Client\RequestHandler\Curl; @@ -27,6 +28,7 @@ /** * @uses \CrowdSec\Common\Client\AbstractClient * @uses \CrowdSec\Common\Client\HttpMessage\Request + * @uses \CrowdSec\Common\Client\HttpMessage\AppSecRequest * @uses \CrowdSec\Common\Client\HttpMessage\Response * @uses \CrowdSec\Common\Client\HttpMessage\AbstractMessage * @@ -34,7 +36,8 @@ * @covers \CrowdSec\Common\Client\RequestHandler\Curl::handle * @covers \CrowdSec\Common\Client\RequestHandler\AbstractRequestHandler::__construct * @covers \CrowdSec\Common\Client\RequestHandler\AbstractRequestHandler::getConfig - * @covers \CrowdSec\Common\Client\RequestHandler\Curl::handleConfigs + * @covers \CrowdSec\Common\Client\RequestHandler\Curl::handleSSL + * @covers \CrowdSec\Common\Client\RequestHandler\Curl::handleTimeout * @covers \CrowdSec\Common\Client\RequestHandler\Curl::handleMethod */ final class CurlTest extends AbstractClient @@ -267,4 +270,78 @@ public function testOptions() 'Curl options must be as expected for DELETE' ); } + + public function testOptionsForAppSec() + { + $url = TestConstants::APPSEC_URL . '/'; + $method = 'POST'; + $headers = ['X-Crowdsec-Appsec-test' => 'test-value']; + $rawBody = 'this is raw body'; + $configs = $this->tlsConfigs; + + $curlRequester = new Curl($configs); + $request = new AppSecRequest($url, 'POST', $headers, $rawBody); + + $curlOptions = PHPUnitUtil::callMethod( + $curlRequester, + 'createOptions', + [$request] + ); + $expected = [ + \CURLOPT_HEADER => false, + \CURLOPT_RETURNTRANSFER => true, + \CURLOPT_HTTPHEADER => [ + 'X-Crowdsec-Appsec-test:test-value', + ], + \CURLOPT_POST => true, + \CURLOPT_POSTFIELDS => 'this is raw body', + \CURLOPT_URL => $url, + \CURLOPT_CUSTOMREQUEST => $method, + \CURLOPT_TIMEOUT => TestConstants::API_TIMEOUT, + \CURLOPT_CONNECTTIMEOUT => Constants::API_CONNECT_TIMEOUT, + \CURLOPT_SSL_VERIFYPEER => false, + \CURLOPT_ENCODING => '', + ]; + + $this->assertEquals( + $expected, + $curlOptions, + 'Curl options must be as expected for POST' + ); + + $method = 'GET'; + $curlRequester = new Curl($configs); + + $request = new AppSecRequest($url, $method, array_merge($headers, ['User-Agent' => TestConstants::USER_AGENT_SUFFIX])); + + $curlOptions = PHPUnitUtil::callMethod( + $curlRequester, + 'createOptions', + [$request] + ); + + $expected = [ + \CURLOPT_HEADER => false, + \CURLOPT_RETURNTRANSFER => true, + \CURLOPT_USERAGENT => TestConstants::USER_AGENT_SUFFIX, + \CURLOPT_HTTPHEADER => [ + 'X-Crowdsec-Appsec-test:test-value', + 'User-Agent:' . TestConstants::USER_AGENT_SUFFIX, + ], + \CURLOPT_POST => false, + \CURLOPT_HTTPGET => true, + \CURLOPT_URL => $url, + \CURLOPT_CUSTOMREQUEST => $method, + \CURLOPT_TIMEOUT => TestConstants::API_TIMEOUT, + \CURLOPT_CONNECTTIMEOUT => Constants::API_CONNECT_TIMEOUT, + \CURLOPT_SSL_VERIFYPEER => false, + \CURLOPT_ENCODING => '', + ]; + + $this->assertEquals( + $expected, + $curlOptions, + 'Curl options must be as expected for GET' + ); + } } diff --git a/tests/Unit/FileGetContentsTest.php b/tests/Unit/FileGetContentsTest.php index 9c35047..a5a0b30 100644 --- a/tests/Unit/FileGetContentsTest.php +++ b/tests/Unit/FileGetContentsTest.php @@ -18,6 +18,7 @@ */ use CrowdSec\Common\Client\ClientException; +use CrowdSec\Common\Client\HttpMessage\AppSecRequest; use CrowdSec\Common\Client\HttpMessage\Request; use CrowdSec\Common\Client\RequestHandler\FileGetContents; use CrowdSec\Common\Constants; @@ -27,10 +28,12 @@ /** * @uses \CrowdSec\Common\Client\AbstractClient * @uses \CrowdSec\Common\Client\HttpMessage\Request + * @uses \CrowdSec\Common\Client\HttpMessage\AppSecRequest * @uses \CrowdSec\Common\Client\HttpMessage\Response * @uses \CrowdSec\Common\Client\HttpMessage\AbstractMessage * * @covers \CrowdSec\Common\Client\RequestHandler\FileGetContents::handle + * @covers \CrowdSec\Common\Client\RequestHandler\FileGetContents::handleSSL * @covers \CrowdSec\Common\Client\RequestHandler\FileGetContents::createContextConfig * @covers \CrowdSec\Common\Client\RequestHandler\FileGetContents::convertHeadersToString * @covers \CrowdSec\Common\Client\RequestHandler\FileGetContents::getResponseHttpCode @@ -154,6 +157,78 @@ public function testContextConfig() ); } + public function testContextConfigForAppSec() + { + $method = 'POST'; + $headers = ['X-CrowdSec-AppSec-test' => 'test-value']; + $rawBody = 'This is a raw body'; + $configs = $this->tlsConfigs; + + $fgcRequester = new FileGetContents($configs); + + $request = new AppSecRequest('test-url', $method, $headers, $rawBody); + + $contextConfig = PHPUnitUtil::callMethod( + $fgcRequester, + 'createContextConfig', + [$request] + ); + + $contextConfig['http']['header'] = str_replace("\r", '', $contextConfig['http']['header']); + + $expected = [ + 'http' => [ + 'method' => $method, + 'header' => 'X-CrowdSec-AppSec-test: test-value +', + 'ignore_errors' => true, + 'content' => 'This is a raw body', + 'timeout' => TestConstants::API_TIMEOUT, + ], + 'ssl' => [ + 'verify_peer' => false, + ], + ]; + + $this->assertEquals( + $expected, + $contextConfig, + 'Context config must be as expected for POST' + ); + + $method = 'GET'; + + $request = new AppSecRequest('test-url', $method, array_merge($headers, ['User-Agent' => TestConstants::USER_AGENT_SUFFIX])); + + $contextConfig = PHPUnitUtil::callMethod( + $fgcRequester, + 'createContextConfig', + [$request] + ); + + $contextConfig['http']['header'] = str_replace("\r", '', $contextConfig['http']['header']); + + $expected = [ + 'http' => [ + 'method' => $method, + 'header' => 'X-CrowdSec-AppSec-test: test-value +User-Agent: ' . TestConstants::USER_AGENT_SUFFIX . ' +', + 'ignore_errors' => true, + 'timeout' => TestConstants::API_TIMEOUT, + ], + 'ssl' => [ + 'verify_peer' => false, + ], + ]; + + $this->assertEquals( + $expected, + $contextConfig, + 'Context config must be as expected for GET' + ); + } + public function testHandleError() { $mockFGCRequest = $this->getFGCMock(['exec']); diff --git a/tests/Unit/RequestTest.php b/tests/Unit/RequestTest.php index 5d0be83..8e30b45 100644 --- a/tests/Unit/RequestTest.php +++ b/tests/Unit/RequestTest.php @@ -15,6 +15,7 @@ * @license MIT License */ +use CrowdSec\Common\Client\HttpMessage\AppSecRequest; use CrowdSec\Common\Client\HttpMessage\Request; use CrowdSec\Common\Tests\Constants as TestConstants; use PHPUnit\Framework\TestCase; @@ -24,6 +25,8 @@ * @covers \CrowdSec\Common\Client\HttpMessage\Request::getMethod * @covers \CrowdSec\Common\Client\HttpMessage\Request::getUri * @covers \CrowdSec\Common\Client\HttpMessage\Request::__construct + * @covers \CrowdSec\Common\Client\HttpMessage\AppSecRequest::__construct + * @covers \CrowdSec\Common\Client\HttpMessage\AppSecRequest::getRawBody * @covers \CrowdSec\Common\Client\HttpMessage\AbstractMessage::getHeaders */ final class RequestTest extends TestCase @@ -71,44 +74,49 @@ public function testConstructor() 'Request headers should be set' ); - $request = new Request( + $request = new AppSecRequest( 'test-uri', 'POST', - ['test' => 'test', 'User-Agent' => TestConstants::USER_AGENT_SUFFIX], - ['foo' => 'bar'], - 'app_sec' + ['test' => 'test'], + 'this is raw body' ); $headers = $request->getHeaders(); $params = $request->getParams(); $method = $request->getMethod(); $uri = $request->getUri(); + $rawBody = $request->getRawBody(); $this->assertEquals( 'POST', $method, - 'Request method should be set' + 'AppSecRequest method should be set' ); $this->assertEquals( 'test-uri', $uri, - 'Request URI should be set' + 'AppSecRequest URI should be set' ); $this->assertEquals( - ['foo' => 'bar'], + [], $params, - 'Request params should be set' + 'AppSecRequest params should be empty' + ); + + $this->assertEquals( + 'this is raw body', + $rawBody, + 'AppSecRequest rawBody should be set' ); $this->assertEquals( [ - 'User-Agent' => TestConstants::USER_AGENT_SUFFIX, 'test' => 'test', ], $headers, - 'Request headers should be set without Content-Type and Accept' + 'AppSecRequest headers should be set without Content-Type and Accept' ); } }