diff --git a/Classes/Client.php b/Classes/Client.php index 83ceeeb..d55ebca 100644 --- a/Classes/Client.php +++ b/Classes/Client.php @@ -146,17 +146,69 @@ protected function checkAccess(ServerRequestInterface $request): string return 'No secret or too small secret defined'; } - if (!isset($settings['allowedIps']) || empty($settings['allowedIps'])) { - return 'No allowed ips defined'; + /* Only returns an error when both are empty */ + if (empty($settings['allowedIps']) && empty($settings['allowedDomains'])) { + return 'No allowed ips or domains defined'; } + + $allowedIps = $this->getAllowedIps($settings['allowedIps'], $settings['allowedDomains']); + $remoteIp = GeneralUtility::getIndpEnv('REMOTE_ADDR'); - if (!GeneralUtility::cmpIP($remoteIp, $settings['allowedIps'])) { + if (!GeneralUtility::cmpIP($remoteIp, $allowedIps)) { return sprintf('IP comparison failed, remote IP: %s!', $remoteIp); } return ''; } + /** + * Parses the allowed domains and IPs from the extension settings into a single allowed IPs string + * @return string The allowed ips, comma separated. + */ + public function getAllowedIps(string $allowedIps, string $allowedDomains): string + { + $allowedIps = trim($allowedIps); + $allowedDomains = trim($allowedDomains); + + /* Return * when anything is allowed in one field, and the other is empty */ + if (($allowedIps === '*' && $allowedDomains === '*') || + ($allowedIps === '*' && empty($allowedDomains)) || + (empty($allowedIps) && $allowedDomains === '*')) { + return '*'; + } + + $allowedIpsResult = ''; + if($allowedIps !== '*' && !empty($allowedIps)) { + $allowedIpsResult = sprintf('%s, ', $allowedIps); + } + + /* Convert the domain names to IP addresses */ + if($allowedDomains !== '*' && !empty($allowedDomains)) { + $allowedDomainsArray = explode(',', $allowedDomains); + foreach ($allowedDomainsArray as $allowedDomain) { + $resultsIpv4 = @dns_get_record($allowedDomain, DNS_A); + if($resultsIpv4) { + foreach ($resultsIpv4 as $resultIpv4) { + if(isset($resultIpv4['ip'])) { + $allowedIpsResult .= sprintf('%s, ', $resultIpv4['ip']); + } + } + } + $resultsIpv6 = @dns_get_record($allowedDomain, DNS_AAAA); + if($resultsIpv6) { + foreach ($resultsIpv6 as $resultIpv6) { + if(isset($resultIpv6['ipv6'])) { + $allowedIpsResult .= sprintf('%s, ', $resultIpv6['ipv6']); + } + } + } + } + } + + /* Return concatenated IPs and domains IPs */ + return trim($allowedIpsResult, ' ,'); + } + protected function getSettings(): array { $configuration = []; diff --git a/README.rst b/README.rst index 6dfa6a4..d6e302a 100644 --- a/README.rst +++ b/README.rst @@ -29,6 +29,10 @@ allowedIps """""""""" Define a comma separated list of IPs which are allowed to fetch the client data. +allowedDomains +"""""""""" +Define a comma separated list of domains which are allowed to fetch the client data. + enableDebugForErrors """""""""""""""""""" If set, the errors are outputted if you call `http://yourdomain.tld/?eID=t3monitoring&secret=`. This can help to identify problems. diff --git a/Tests/Unit/ClientTest.php b/Tests/Unit/ClientTest.php new file mode 100644 index 0000000..0c25243 --- /dev/null +++ b/Tests/Unit/ClientTest.php @@ -0,0 +1,148 @@ +client = GeneralUtility::makeInstance(Client::class); + } + + /** + * @test + */ + public function testInputIpsAndDomainsStarsReturnOneStarOnly() + { + $allowedIps = '*'; + $allowedDomains = '*'; + $this->assertEquals('*', $this->client->getAllowedIps($allowedIps, $allowedDomains)); + + $allowedIps = '*'; + $allowedDomains = ''; + $this->assertEquals('*', $this->client->getAllowedIps($allowedIps, $allowedDomains)); + + $allowedIps = ''; + $allowedDomains = '*'; + $this->assertEquals('*', $this->client->getAllowedIps($allowedIps, $allowedDomains)); + } + + /** + * @test + */ + public function testValuesHasPriorityOverStarOverEmpty() + { + $allowedIps = '78.47.171.202, 142.250.184.206'; + $allowedDomains = '*'; + /* Expect the IPs to be returned */ + $this->assertCount( + 2, + explode(',', $this->client->getAllowedIps($allowedIps, $allowedDomains)) + ); + + $allowedIps = '78.47.171.202, 142.250.184.206'; + $allowedDomains = ''; + /* Expect the IPs to be returned */ + $this->assertCount( + 2, + explode(',', $this->client->getAllowedIps($allowedIps, $allowedDomains)) + ); + + $allowedIps = '*'; + $allowedDomains = 'www.google.com, www.test.io'; + /* Expect the IPs from the domains to be returned */ + $this->assertGreaterThanOrEqual( + 2, + explode(',', $this->client->getAllowedIps($allowedIps, $allowedDomains)) + ); + + $allowedIps = ''; + $allowedDomains = 'www.google.com, www.test.io'; + /* Expect the IPs from the domains to be returned */ + $this->assertGreaterThanOrEqual( + 2, + explode(',', $this->client->getAllowedIps($allowedIps, $allowedDomains)) + ); + } + + /** + * @test + */ + public function testInputDomainsOnlyReturnsIPv4AndIPv6() + { + $allowedIps = ''; + $allowedDomains = 'www.google.com'; + + /* Expect the IPs to be returned */ + $this->assertGreaterThanOrEqual( + 1, + explode(',', $this->client->getAllowedIps($allowedIps, $allowedDomains)) + ); + } + + /** + * @test + */ + public function testInputIpsAndDomainsReturnsBothConcatenated() + { + $allowedIps = '78.47.171.202, 142.250.184.206'; + $allowedDomains = 'www.google.com, www.test.io'; + + /* Expect 4 or more IPs (depending on IPv6 implementation and ) to be returned */ + $this->assertGreaterThanOrEqual( + 4, + explode(',', $this->client->getAllowedIps($allowedIps, $allowedDomains)) + ); + } + + /** + * @test + */ + public function testExpectTypeErrorOnNull() + { + $allowedIps = null; + $allowedDomains = null; + + $this->expectException(TypeError::class); + $this->client->getAllowedIps($allowedIps, $allowedDomains); + } + + /** + * @test + */ + public function testExpectZeroIpsWithEmptyParameters() + { + $allowedIps = ''; + $allowedDomains = ''; + + $this->assertEquals( + '', + $this->client->getAllowedIps($allowedIps, $allowedDomains) + ); + } + + /** + * @test + */ + public function testExpectBogusDomainNamesToNotBeProcessed() + { + $allowedIps = '78.47.171.202, 142.250.184.206'; + $allowedDomains = 'com.google.www, io.test.www, test@test.com, www,test,io, www/test/io, jksdafjsdnidmfhsnbsdfkjnjuhasghdsn.nl'; + + $this->assertCount( + 2, + explode(',', $this->client->getAllowedIps($allowedIps, $allowedDomains)) + ); + } + +} diff --git a/ext_conf_template.txt b/ext_conf_template.txt index 7c34928..f96061c 100644 --- a/ext_conf_template.txt +++ b/ext_conf_template.txt @@ -6,4 +6,7 @@ secret = allowedIps = * # cat=features/enable/3; type=boolean; label=Enable debugging: Instead of silently stopping the endpoint call, the error is outputted. Just enable this to debug if a connection is not possible! -enableDebugForErrors = 0 \ No newline at end of file +enableDebugForErrors = 0 + +# cat=features/enable/2; type=string; label=Allowed domains:Restrict the endpoint to specific domains. Comma separated lists are possible +allowedDomains = *