diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2a5103b..d3311e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,9 @@ jobs: - uses: php-actions/composer@v1 - uses: nanasess/setup-php@master - uses: php-actions/phpunit@v9 + with: + configuration: ./phpunit.xml + args: --coverage-text - name: Publish to codecov run: bash <(curl -s https://codecov.io/bash) @@ -54,4 +57,4 @@ jobs: message_id: ${{ steps.slack.outputs.message_id }} channel: github-actions status: FAILED - color: danger + color: danger \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d7d1b4..a64778a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,9 @@ jobs: - uses: php-actions/composer@v1 - uses: nanasess/setup-php@master - uses: php-actions/phpunit@v9 + with: + configuration: ./phpunit.xml + args: --coverage-text - name: Publish to codecov run: bash <(curl -s https://codecov.io/bash) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index edf036a..728c02e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,7 +3,7 @@ name: Publish on: release: types: [created] - + jobs: release: runs-on: ubuntu-latest @@ -22,10 +22,6 @@ jobs: - uses: actions/checkout@v2 - uses: php-actions/composer@v1 - uses: nanasess/setup-php@master - - uses: php-actions/phpunit@v9 - - - name: Publish to codecov - run: bash <(curl -s https://codecov.io/bash) - name: Publish uses: musps/action-deployer-php@master @@ -33,6 +29,11 @@ jobs: env: SSH_PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + - name: Create changelog + uses: heinrichreimer/github-changelog-generator-action@v2.1.1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Notify slack success if: success() env: diff --git a/.phpunit.result.cache b/.phpunit.result.cache new file mode 100644 index 0000000..c39587a --- /dev/null +++ b/.phpunit.result.cache @@ -0,0 +1 @@ +C:37:"PHPUnit\Runner\DefaultTestResultCache":5287:{a:2:{s:7:"defects";a:36:{s:30:"AgentTest::testApiKeyException";i:4;s:18:"ApiTest::testTrack";i:4;s:19:"ApiTest::testVerify";i:4;s:44:"ConfigurationManagerTest::testReadConfigFile";i:3;s:50:"ConfigurationManagerTest::testEnvironmentVariables";i:4;s:43:"ConfigurationManagerTest::testDefaultParams";i:3;s:58:"ConfigurationManagerTest::testEnvironmentVariablesOverride";i:4;s:34:"EventManagerTest::testEventOptions";i:4;s:32:"EventManagerTest::testBuildEvent";i:4;s:30:"EventManagerTest::testSendSync";i:4;s:34:"EventManagerTest::testSendFailSync";i:4;s:38:"EventManagerTest::testAsyncShouldRetry";i:4;s:31:"EventManagerTest::testSendAsync";i:4;s:62:"RequestUtilsTest::testIpExtractionUsingXFORWARDEDFORHeaderIpv6";i:3;s:70:"RequestUtilsTest::testIpExtractionUsingXFORWARDEDFORHeaderMultipleIpv4";i:3;s:68:"RequestUtilsTest::testIpExtractionUsingHTTPXREALIPHeaderMultipleIpv4";i:3;s:59:"RequestUtilsTest::testIpExtractionUsingREMOTEADDRHeaderIpv6";i:3;s:67:"RequestUtilsTest::testIpExtractionUsingREMOTEADDRHeaderMultipleIpv4";i:3;s:58:"RequestUtilsTest::testIpExtractionUsingXClientIpHeaderIpv6";i:3;s:66:"RequestUtilsTest::testIpExtractionUsingXClientIpHeaderMultipleIpv4";i:3;s:56:"RequestUtilsTest::testIpExtractionUsingXRealIpHeaderIpv6";i:3;s:64:"RequestUtilsTest::testIpExtractionUsingXRealIpHeaderMultipleIpv4";i:3;s:61:"RequestUtilsTest::testIpExtractionUsingForwardedForHeaderIpv6";i:3;s:69:"RequestUtilsTest::testIpExtractionUsingForwardedForHeaderMultipleIpv4";i:3;s:65:"RequestUtilsTest::testIpExtractionUsingXClusterClientIpHeaderIpv6";i:3;s:73:"RequestUtilsTest::testIpExtractionUsingXClusterClientIpHeaderMultipleIpv4";i:3;s:59:"RequestUtilsTest::testIpExtractionUsingXForwardedHeaderIpv6";i:3;s:67:"RequestUtilsTest::testIpExtractionUsingXForwardedHeaderMultipleIpv4";i:3;s:58:"RequestUtilsTest::testIpExtractionUsingForwardedHeaderIpv6";i:3;s:66:"RequestUtilsTest::testIpExtractionUsingForwardedHeaderMultipleIpv4";i:3;s:52:"RequestUtilsTest::testIpExtractionUsingViaHeaderIpv6";i:3;s:60:"RequestUtilsTest::testIpExtractionUsingViaHeaderMultipleIpv4";i:3;s:62:"RequestUtilsTest::testIpExtractionUsingHTTPXCLIENTIPHeaderIpv6";i:3;s:70:"RequestUtilsTest::testIpExtractionUsingHTTPXCLIENTIPHeaderMultipleIpv4";i:3;s:60:"RequestUtilsTest::testIpExtractionUsingHTTPXREALIPHeaderIpv6";i:3;s:60:"RequestUtilsTest::testExtractionPriorityWithoutXForwardedFor";i:3;}s:5:"times";a:45:{s:30:"AgentTest::testApiKeyException";d:0.023;s:18:"ApiTest::testTrack";d:0.015;s:31:"ApiTest::testTrackCustomContext";d:0.001;s:19:"ApiTest::testVerify";d:0.001;s:44:"ConfigurationManagerTest::testReadConfigFile";d:0.001;s:41:"ConfigurationManagerTest::testUnknownKeys";d:0.001;s:41:"ConfigurationManagerTest::testInvalidFile";d:0.001;s:48:"ConfigurationManagerTest::testInvalidFileEntries";d:0.001;s:40:"ConfigurationManagerTest::testLoadConfig";d:0;s:50:"ConfigurationManagerTest::testEnvironmentVariables";d:0;s:43:"ConfigurationManagerTest::testDefaultParams";d:0;s:58:"ConfigurationManagerTest::testEnvironmentVariablesOverride";d:0;s:34:"EventManagerTest::testEventOptions";d:0;s:32:"EventManagerTest::testBuildEvent";d:0;s:30:"EventManagerTest::testSendSync";d:0.002;s:34:"EventManagerTest::testSendFailSync";d:0.002;s:38:"EventManagerTest::testAsyncShouldRetry";d:0.002;s:31:"EventManagerTest::testSendAsync";d:0.001;s:42:"RequestUtilsTest::testProxyHeadersWithIpv4";d:0;s:42:"RequestUtilsTest::testProxyHeadersWithIpv6";d:0;s:53:"RequestUtilsTest::testProxyHeadersWithMultipleHeaders";d:0;s:62:"RequestUtilsTest::testIpExtractionUsingXFORWARDEDFORHeaderIpv6";d:0;s:70:"RequestUtilsTest::testIpExtractionUsingXFORWARDEDFORHeaderMultipleIpv4";d:0;s:60:"RequestUtilsTest::testIpExtractionUsingHTTPXREALIPHeaderIpv6";d:0;s:68:"RequestUtilsTest::testIpExtractionUsingHTTPXREALIPHeaderMultipleIpv4";d:0;s:59:"RequestUtilsTest::testIpExtractionUsingREMOTEADDRHeaderIpv6";d:0;s:67:"RequestUtilsTest::testIpExtractionUsingREMOTEADDRHeaderMultipleIpv4";d:0;s:58:"RequestUtilsTest::testIpExtractionUsingXClientIpHeaderIpv6";d:0;s:66:"RequestUtilsTest::testIpExtractionUsingXClientIpHeaderMultipleIpv4";d:0;s:56:"RequestUtilsTest::testIpExtractionUsingXRealIpHeaderIpv6";d:0;s:64:"RequestUtilsTest::testIpExtractionUsingXRealIpHeaderMultipleIpv4";d:0;s:61:"RequestUtilsTest::testIpExtractionUsingForwardedForHeaderIpv6";d:0;s:69:"RequestUtilsTest::testIpExtractionUsingForwardedForHeaderMultipleIpv4";d:0;s:65:"RequestUtilsTest::testIpExtractionUsingXClusterClientIpHeaderIpv6";d:0;s:73:"RequestUtilsTest::testIpExtractionUsingXClusterClientIpHeaderMultipleIpv4";d:0;s:59:"RequestUtilsTest::testIpExtractionUsingXForwardedHeaderIpv6";d:0;s:67:"RequestUtilsTest::testIpExtractionUsingXForwardedHeaderMultipleIpv4";d:0;s:58:"RequestUtilsTest::testIpExtractionUsingForwardedHeaderIpv6";d:0;s:66:"RequestUtilsTest::testIpExtractionUsingForwardedHeaderMultipleIpv4";d:0;s:52:"RequestUtilsTest::testIpExtractionUsingViaHeaderIpv6";d:0;s:60:"RequestUtilsTest::testIpExtractionUsingViaHeaderMultipleIpv4";d:0;s:62:"RequestUtilsTest::testIpExtractionUsingHTTPXCLIENTIPHeaderIpv6";d:0;s:70:"RequestUtilsTest::testIpExtractionUsingHTTPXCLIENTIPHeaderMultipleIpv4";d:0;s:57:"RequestUtilsTest::testExtractionPriorityWithXForwardedFor";d:0;s:60:"RequestUtilsTest::testExtractionPriorityWithoutXForwardedFor";d:0;}}} \ No newline at end of file diff --git a/README.md b/README.md index 4e27378..6f3f21c 100644 --- a/README.md +++ b/README.md @@ -68,9 +68,9 @@ Attach `securenative.json` file to your root folder: ```json { - "SECURENATIVE_API_KEY": "SOME_API_KEY", - "SECURENATIVE_APP_NAME": "SOME_APP_NAME", - "SECURENATIVE_API_URL": "SOME_API_URL", + "SECURENATIVE_API_KEY": "YOUR_API_KEY", + "SECURENATIVE_APP_NAME": "APP_NAME", + "SECURENATIVE_API_URL": "API_URL", "SECURENATIVE_INTERVAL": 1000, "SECURENATIVE_MAX_EVENTS": 100, "SECURENATIVE_TIMEOUT": 1500, @@ -161,10 +161,12 @@ SecureNative::track(array( **Example** ```php +$options = new SecureNativeOptions(); + $ver = SecureNative::verify(array( 'event' => EventTypes::VERIFY, 'userId' => '1234', - 'context' => SecureNativeContext::fromRequest(), + 'context' => SecureNative::fromRequest(), 'userTraits' => (object)[ 'name' => 'Your Name', 'email' => 'name@gmail.com' @@ -187,3 +189,27 @@ if ($verified) { // Request is trusted (coming from SecureNative) } ``` + +## Extract proxy headers from cloud providers + +You can specify custom header keys to allow extraction of client ip from different providers. +This example demonstrates the usage of proxy headers for ip extraction from Cloudflare. + +### Option 1: Using config file +```json +{ + "SECURENATIVE_API_KEY": "YOUR_API_KEY", + "SECURENATIVE_PROXY_HEADERS": ["CF-Connecting-IP"] +} +``` + +Initialize sdk as shown above. + +### Options 2: Using ConfigurationBuilder + +```php +$options = new SecureNativeOptions(); +$options->setProxyHeaders(["CF-Connecting-IP"]); + +SecureNative::init(); +``` \ No newline at end of file diff --git a/build/logs/clover.xml b/build/logs/clover.xml new file mode 100644 index 0000000..10aa9d4 --- /dev/null +++ b/build/logs/clover.xml @@ -0,0 +1,660 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/composer.json b/composer.json index 8377580..36bfc8c 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,9 @@ "guzzlehttp/guzzle": "^6.0", "antecedent/patchwork": "~2.0", "monolog/monolog": "2.0.2", - "phpunit/phpunit": "^9" + "phpunit/phpunit": "^9", + "ext-openssl": "*", + "ext-json": "*" }, "require-dev": { "php-coveralls/php-coveralls": "*" diff --git a/phpunit.xml b/phpunit.xml index cc955a1..31788df 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,5 +1,5 @@ - + ./tests diff --git a/src/ConfigurationManager.php b/src/ConfigurationManager.php index 1510265..f3d9b55 100644 --- a/src/ConfigurationManager.php +++ b/src/ConfigurationManager.php @@ -15,6 +15,7 @@ function getConfigMap() 'SECURENATIVE_DISABLE' => (object)['name' => 'disable', 'type' => 'boolean'], 'SECURENATIVE_LOG_LEVEL' => (object)['name' => 'logLevel', 'type' => 'string'], 'SECURENATIVE_FAILOVER_STRATEGY' => (object)['name' => 'failoverStrategy', 'type' => 'string'], + 'SECURENATIVE_PROXY_HEADERS' => (object)['name' => 'proxyHeaders', 'type' => 'array'], ]; } @@ -88,6 +89,8 @@ private static function loadConfig($configFilePath = null) $envVal = intval($envVal); } else if ($type == "boolean") { $envVal = boolval($envVal); + } else if ($type == "array") { + $envVal = explode(',', $envVal); } } @@ -104,6 +107,7 @@ private static function loadConfig($configFilePath = null) $getConfigOrEnv('disable', 'SECURENATIVE_DISABLE'), $getConfigOrEnv('logLevel', 'SECURENATIVE_LOG_LEVEL'), $getConfigOrEnv('failoverStrategy', 'SECURENATIVE_FAILOVER_STRATEGY'), + $getConfigOrEnv('proxyHeaders', 'SECURENATIVE_PROXY_HEADERS'), ); } diff --git a/src/SecureNative.php b/src/SecureNative.php index 3a195bd..fc9ddfb 100644 --- a/src/SecureNative.php +++ b/src/SecureNative.php @@ -95,20 +95,6 @@ public static function flow($flowId, Array $attributes) return self::$eventManager->sendSync($event, $requestUrl); } - public static function getRequestContext() - { - return SecureNativeContext::fromRequest(); - } - - /** - * @deprecated use `getRequestContext()` instead - * @return SecureNativeContext - */ - public static function contextFromContext() - { - return SecureNativeContext::fromRequest(); - } - public static function getMiddleware() { return self::$middleware; @@ -122,6 +108,14 @@ public static function getApiKey() return self::$apiKey; } + /** + * @return SecureNativeContext + */ + public static function fromRequest() + { + return SecureNativeContext::fromRequest(self::$options); + } + /** * Destroy object after use */ diff --git a/src/SecureNativeContext.php b/src/SecureNativeContext.php index fe7ea94..57db7e4 100644 --- a/src/SecureNativeContext.php +++ b/src/SecureNativeContext.php @@ -35,11 +35,11 @@ public function __construct($clientToken, $ip = null, $remoteIp = null, $headers $this->body = $body; } - public static function fromRequest() : SecureNativeContext + public static function fromRequest($options) : SecureNativeContext { - $clientToken = Utils::cookieIdFromRequest() ? Utils::cookieIdFromRequest() : Utils::securHeaderFromRequest(); - $ip = Utils::clientIpFromRequest(); - $remoteIp = Utils::clientIpFromRequest(); + $clientToken = Utils::cookieIdFromRequest() ? Utils::cookieIdFromRequest() : Utils::secureHeaderFromRequest(); + $ip = Utils::clientIpFromRequest($options); + $remoteIp = Utils::clientIpFromRequest($options); $headers = Utils::headersFromRequest(); $url = Utils::urlFromRequest(); $method = Utils::methodFromRequest(); diff --git a/src/SecureNativeOptions.php b/src/SecureNativeOptions.php index 4e9da63..742fce7 100644 --- a/src/SecureNativeOptions.php +++ b/src/SecureNativeOptions.php @@ -14,6 +14,7 @@ class SecureNativeOptions private $disable; private $logLevel; private $failoverStrategy; + private $proxyHeaders; // Needed for option merging private $default = array( @@ -26,9 +27,10 @@ class SecureNativeOptions "disable" => false, "logLevel" => 'fatal', "failoverStrategy" => 'fail-open', + "proxyHeaders" => array(), ); - public function __construct($apiKey = null, $apiUrl = null, $interval = null, $maxEvents = null, $timeout = null, $autoSend = null, $disable = null, $logLevel = null, $failoverStrategy = null) + public function __construct($apiKey = null, $apiUrl = null, $interval = null, $maxEvents = null, $timeout = null, $autoSend = null, $disable = null, $logLevel = null, $failoverStrategy = null, $proxyHeaders = null) { $this->apiKey = isset($apiKey) ? $apiKey : $this->default["apiKey"]; $this->apiUrl = isset($apiUrl) ? $apiUrl : $this->default["apiUrl"]; @@ -39,6 +41,7 @@ public function __construct($apiKey = null, $apiUrl = null, $interval = null, $m $this->disable = isset($disable) ? $disable : $this->default["disable"]; $this->logLevel = isset($logLevel) ? $logLevel : $this->default["logLevel"]; $this->failoverStrategy = isset($failoverStrategy) ? $failoverStrategy : $this->default["failoverStrategy"]; + $this->proxyHeaders = isset($proxyHeaders) ? $proxyHeaders : $this->default["proxyHeaders"]; } /** @@ -203,6 +206,24 @@ public function setFailoverStrategy(string $failoverStrategy): SecureNativeOptio return $this; } + /** + * @return array + */ + public function getProxyHeaders(): array + { + return $this->proxyHeaders; + } + + /** + * @param array $proxyHeaders + * @return SecureNativeOptions + */ + public function setProxyHeaders(array $proxyHeaders): SecureNativeOptions + { + $this->proxyHeaders = $proxyHeaders; + return $this; + } + /** * Merge an existing `SecureNativeOptions` object into an existing one * @param SecureNativeOptions $options @@ -217,5 +238,6 @@ public function mergeOptions(SecureNativeOptions $options) { $options->isDisable() != $this->default["disable"] ? $this->setDisable($options->isDisable()) : null; $options->getLogLevel() != $this->default["logLevel"] ? $this->setLogLevel($options->getLogLevel()) : null; $options->getFailoverStrategy() != $this->default["failoverStrategy"] ? $this->setFailoverStrategy($options->getFailoverStrategy()) : null; + $options->getProxyHeaders() != $this->default["proxyHeaders"] ? $this->setProxyHeaders($options->getProxyHeaders()) : null; } } diff --git a/src/Utils.php b/src/Utils.php index 18998f7..617a8de 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -5,22 +5,37 @@ const ALGORITHM = "AES-256-CBC"; const BLOCK_SIZE = 16; const AES_KEY_SIZE = 32; +const IP_HEADERS = ["HTTP_X_FORWARDED_FOR", "X_FORWARDED_FOR", "HTTP_X_CLIENT_IP", "HTTP_X_REAL_IP", "REMOTE_ADDR", "x-forwarded-for", "x-client-ip", "x-real-ip", "x-forwarded", "x-cluster-client-ip", "forwarded-for", "forwarded", "via"]; abstract class Utils { - public static function clientIpFromRequest() + public static function clientIpFromRequest($options) { - if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) { - $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); - return $parts[0]; - } - if (array_key_exists('HTTP_X_REAL_IP', $_SERVER)) { - return $_SERVER['HTTP_X_REAL_IP']; + if (!empty($options) && count($options->getProxyHeaders()) > 0) { + foreach ($options->getProxyHeaders() as $header) { + if (array_key_exists($header, $_SERVER)) { + $parts = explode(',', $_SERVER[$header]); + foreach ($parts as $ip) { + if (filter_var($ip, FILTER_VALIDATE_IP)) { + return $ip; + } + } + } + } } - if (array_key_exists('REMOTE_ADDR', $_SERVER)) { - return $_SERVER['REMOTE_ADDR']; + + foreach (IP_HEADERS as $header) { + if (array_key_exists($header, $_SERVER)) { + $parts = explode(',', $_SERVER[$header]); + foreach ($parts as $ip) { + if (filter_var($ip, FILTER_VALIDATE_IP)) { + return $ip; + } + } + } } + return null; } @@ -63,7 +78,7 @@ public static function methodFromRequest() return ''; } - public static function securHeaderFromRequest() + public static function secureHeaderFromRequest() { if (array_key_exists('HTTP_X_SECURENATIVE', $_SERVER)) { return $_SERVER['HTTP_X_SECURENATIVE']; @@ -122,4 +137,4 @@ public static function encrypt($plainText, $cipherKey) { } } -} +} \ No newline at end of file diff --git a/tests/AgentTest.php b/tests/AgentTest.php index ff38425..efdf717 100644 --- a/tests/AgentTest.php +++ b/tests/AgentTest.php @@ -1,8 +1,6 @@ EventTypes::LOG_IN, diff --git a/tests/ApiTest.php b/tests/ApiTest.php index 8191558..0dc38d7 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -1,15 +1,9 @@ assertArrayHasKey('autoSend', $arr); $this->assertArrayHasKey('disable', $arr); $this->assertArrayHasKey('logLevel', $arr); + $this->assertArrayHasKey('proxyHeaders', $arr); } public function testUnknownKeys() @@ -91,7 +92,7 @@ public function testEnvironmentVariables() { $testKeys = $this->getConfigTestKeys(); - // Set env for each ovject item + // Set env for each object item foreach ($testKeys as $key => $item) { putenv("$key=" . $item->value); } @@ -125,7 +126,7 @@ public function testEnvironmentVariablesOverride() { $testKeys = $this->getConfigTestKeys(); - // Set env for each ovject item + // Set env for each object item foreach ($testKeys as $key => $item) { putenv("$key=" . $item->value); } @@ -142,6 +143,7 @@ public function testEnvironmentVariablesOverride() $this->assertEquals(true, $options->isAutoSend()); $this->assertEquals(false, $options->isDisable()); $this->assertEquals("fatal", $options->getLogLevel()); + $this->assertEquals(["CF-Connecting-IP", "Some-Random-IP"], $options->getProxyHeaders()); foreach ($testKeys as $key => $item) { // Clear env variables after test diff --git a/tests/EventManagerTest.php b/tests/EventManagerTest.php index 0dafafd..1e78417 100644 --- a/tests/EventManagerTest.php +++ b/tests/EventManagerTest.php @@ -105,7 +105,6 @@ public function testAsyncShouldRetry() { $this->assertNull($callbackRes, 'Track callback should not be called'); } - public function testSendAsync() { $options = new SecureNativeOptions(self::TEST_API_KEY, "http://testushim.com"); @@ -133,5 +132,4 @@ public function testSendAsync() $this->assertObjectHasAttribute('prop1', $callbackRes->{'properties'}); $this->assertObjectHasAttribute('prop2', $callbackRes->{'properties'}); } - } diff --git a/tests/MockUtils.php b/tests/MockUtils.php index da25df9..25782f9 100644 --- a/tests/MockUtils.php +++ b/tests/MockUtils.php @@ -6,6 +6,7 @@ use GuzzleHttp\Psr7\Response; use SecureNative\sdk\EventTypes; use SecureNative\sdk\SecureNative; +use SecureNative\sdk\SecureNativeOptions; function getClient($fail = false) { @@ -27,9 +28,10 @@ function getClient($fail = false) function mock_track_object($context = null) { + $options = new SecureNativeOptions(); return array( 'event' => EventTypes::LOG_IN, - 'context' => isset($context) ? $context : SecureNative::getRequestContext(), + 'context' => isset($context) ? $context : SecureNative::fromRequest($options), 'userId' => '556595', 'userTraits' => (object)[ 'name' => 'Your name', diff --git a/tests/RequestUtilsTest.php b/tests/RequestUtilsTest.php new file mode 100644 index 0000000..a899e80 --- /dev/null +++ b/tests/RequestUtilsTest.php @@ -0,0 +1,290 @@ +setProxyHeaders(["CF-Connecting-IP"]); + + $_SERVER["CF-Connecting-IP"] = "203.0.113.1"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["CF-Connecting-IP"]); + $this->assertEquals("203.0.113.1", $client_ip); + } + + public function testProxyHeadersWithIpv6() + { + $options = new SecureNativeOptions(); + $options->setProxyHeaders(["CF-Connecting-IP"]); + + $_SERVER["CF-Connecting-IP"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["CF-Connecting-IP"]); + $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip); + } + + public function testProxyHeadersWithMultipleHeaders() + { + $options = new SecureNativeOptions(); + $options->setProxyHeaders(["CF-Connecting-IP"]); + + $_SERVER["CF-Connecting-IP"] = "141.246.115.116, 203.0.113.1, 12.34.56.3"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["CF-Connecting-IP"]); + $this->assertEquals("141.246.115.116", $client_ip); + } + + public function testIpExtractionUsingXFORWARDEDFORHeaderIpv6() + { + $options = new SecureNativeOptions(); + $_SERVER["X_FORWARDED_FOR"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["X_FORWARDED_FOR"]); + $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip); + } + + public function testIpExtractionUsingXFORWARDEDFORHeaderMultipleIpv4() + { + $options = new SecureNativeOptions(); + $_SERVER["X_FORWARDED_FOR"] = "141.246.115.116, 203.0.113.1, 12.34.56.3"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["X_FORWARDED_FOR"]); + $this->assertEquals("141.246.115.116", $client_ip); + } + + public function testIpExtractionUsingHTTPXREALIPHeaderIpv6() + { + $options = new SecureNativeOptions(); + $_SERVER["HTTP_X_REAL_IP"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["HTTP_X_REAL_IP"]); + $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip); + } + + public function testIpExtractionUsingHTTPXREALIPHeaderMultipleIpv4() + { + $options = new SecureNativeOptions(); + $_SERVER["HTTP_X_REAL_IP"] = "141.246.115.116, 203.0.113.1, 12.34.56.3"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["HTTP_X_REAL_IP"]); + $this->assertEquals("141.246.115.116", $client_ip); + } + + public function testIpExtractionUsingREMOTEADDRHeaderIpv6() + { + $options = new SecureNativeOptions(); + $_SERVER["REMOTE_ADDR"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["REMOTE_ADDR"]); + $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip); + } + + public function testIpExtractionUsingREMOTEADDRHeaderMultipleIpv4() + { + $options = new SecureNativeOptions(); + $_SERVER["REMOTE_ADDR"] = "141.246.115.116, 203.0.113.1, 12.34.56.3"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["REMOTE_ADDR"]); + $this->assertEquals("141.246.115.116", $client_ip); + } + + public function testIpExtractionUsingXClientIpHeaderIpv6() + { + $options = new SecureNativeOptions(); + $_SERVER["x-client-ip"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["x-client-ip"]); + $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip); + } + + public function testIpExtractionUsingXClientIpHeaderMultipleIpv4() + { + $options = new SecureNativeOptions(); + $_SERVER["x-client-ip"] = "141.246.115.116, 203.0.113.1, 12.34.56.3"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["x-client-ip"]); + $this->assertEquals("141.246.115.116", $client_ip); + } + + public function testIpExtractionUsingXRealIpHeaderIpv6() + { + $options = new SecureNativeOptions(); + $_SERVER["x-real-ip"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["x-real-ip"]); + $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip); + } + + public function testIpExtractionUsingXRealIpHeaderMultipleIpv4() + { + $options = new SecureNativeOptions(); + $_SERVER["x-real-ip"] = "141.246.115.116, 203.0.113.1, 12.34.56.3"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["x-real-ip"]); + $this->assertEquals("141.246.115.116", $client_ip); + } + + public function testIpExtractionUsingForwardedForHeaderIpv6() + { + $options = new SecureNativeOptions(); + $_SERVER["forwarded-for"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["forwarded-for"]); + $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip); + } + + public function testIpExtractionUsingForwardedForHeaderMultipleIpv4() + { + $options = new SecureNativeOptions(); + $_SERVER["forwarded-for"] = "141.246.115.116, 203.0.113.1, 12.34.56.3"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["forwarded-for"]); + $this->assertEquals("141.246.115.116", $client_ip); + } + + public function testIpExtractionUsingXClusterClientIpHeaderIpv6() + { + $options = new SecureNativeOptions(); + $_SERVER["x-cluster-client-ip"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["x-cluster-client-ip"]); + $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip); + } + + public function testIpExtractionUsingXClusterClientIpHeaderMultipleIpv4() + { + $options = new SecureNativeOptions(); + $_SERVER["x-cluster-client-ip"] = "141.246.115.116, 203.0.113.1, 12.34.56.3"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["x-cluster-client-ip"]); + $this->assertEquals("141.246.115.116", $client_ip); + } + + public function testIpExtractionUsingXForwardedHeaderIpv6() + { + $options = new SecureNativeOptions(); + $_SERVER["x-forwarded"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["x-forwarded"]); + $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip); + } + + public function testIpExtractionUsingXForwardedHeaderMultipleIpv4() + { + $options = new SecureNativeOptions(); + $_SERVER["x-forwarded"] = "141.246.115.116, 203.0.113.1, 12.34.56.3"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["x-forwarded"]); + $this->assertEquals("141.246.115.116", $client_ip); + } + + public function testIpExtractionUsingForwardedHeaderIpv6() + { + $options = new SecureNativeOptions(); + $_SERVER["forwarded"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["forwarded"]); + $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip); + } + + public function testIpExtractionUsingForwardedHeaderMultipleIpv4() + { + $options = new SecureNativeOptions(); + $_SERVER["forwarded"] = "141.246.115.116, 203.0.113.1, 12.34.56.3"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["forwarded"]); + $this->assertEquals("141.246.115.116", $client_ip); + } + + public function testIpExtractionUsingViaHeaderIpv6() + { + $options = new SecureNativeOptions(); + $_SERVER["via"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["via"]); + $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip); + } + + public function testIpExtractionUsingViaHeaderMultipleIpv4() + { + $options = new SecureNativeOptions(); + $_SERVER["via"] = "141.246.115.116, 203.0.113.1, 12.34.56.3"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["via"]); + $this->assertEquals("141.246.115.116", $client_ip); + } + + public function testIpExtractionUsingHTTPXCLIENTIPHeaderIpv6() + { + $options = new SecureNativeOptions(); + $_SERVER["HTTP_X_CLIENT_IP"] = "f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["HTTP_X_CLIENT_IP"]); + $this->assertEquals("f71f:5bf9:25ff:1883:a8c4:eeff:7b80:aa2d", $client_ip); + } + + public function testIpExtractionUsingHTTPXCLIENTIPHeaderMultipleIpv4() + { + $options = new SecureNativeOptions(); + $_SERVER["HTTP_X_CLIENT_IP"] = "141.246.115.116, 203.0.113.1, 12.34.56.3"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["HTTP_X_CLIENT_IP"]); + $this->assertEquals("141.246.115.116", $client_ip); + } + + public function testExtractionPriorityWithXForwardedFor() + { + $options = new SecureNativeOptions(); + $_SERVER["HTTP_X_FORWARDED_FOR"] = "203.0.113.1"; + $_SERVER["HTTP_X_REAL_IP"] = "198.51.100.101"; + $_SERVER["HTTP_X_CLIENT_IP"] = "198.51.100.102"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["HTTP_X_FORWARDED_FOR"]); + unset($_SERVER["HTTP_X_REAL_IP"]); + unset($_SERVER["HTTP_X_CLIENT_IP"]); + $this->assertEquals("203.0.113.1", $client_ip); + } + + public function testExtractionPriorityWithoutXForwardedFor() + { + $options = new SecureNativeOptions(); + $_SERVER["HTTP_X_REAL_IP"] = "198.51.100.101"; + $_SERVER["HTTP_X_CLIENT_IP"] = "203.0.113.1, 141.246.115.116, 12.34.56.3"; + + $client_ip = Utils::clientIpFromRequest($options); + unset($_SERVER["HTTP_X_REAL_IP"]); + unset($_SERVER["HTTP_X_CLIENT_IP"]); + $this->assertEquals("203.0.113.1", $client_ip); + } +} \ No newline at end of file diff --git a/tests/SecureNativeTest.php b/tests/SecureNativeTest.php index 5b95e18..dce2bc6 100644 --- a/tests/SecureNativeTest.php +++ b/tests/SecureNativeTest.php @@ -1,11 +1,9 @@ - + .