diff --git a/README.md b/README.md index 03c62411..18cf02fd 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Each API call is provided via a similarly named function within various classes - [x] Zones - [x] User Administration (partial) - [x] [Cloudflare IPs](https://www.cloudflare.com/ips/) -- [ ] [Page Rules](https://support.cloudflare.com/hc/en-us/articles/200168306-Is-there-a-tutorial-for-Page-Rules-) +- [x] [Page Rules](https://support.cloudflare.com/hc/en-us/articles/200168306-Is-there-a-tutorial-for-Page-Rules-) - [ ] [Web Application Firewall (WAF)](https://www.cloudflare.com/waf/) - [ ] Virtual DNS Management - [ ] Custom hostnames diff --git a/src/Configurations/ConfigurationsException.php b/src/Configurations/ConfigurationsException.php new file mode 100644 index 00000000..ece16876 --- /dev/null +++ b/src/Configurations/ConfigurationsException.php @@ -0,0 +1,15 @@ +id = "always_online"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setAlwaysUseHTTPS(bool $active) + { + $object = new \stdClass(); + $object->id = "always_use_https"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setBrowserCacheTTL(int $ttl) + { + $object = new \stdClass(); + $object->id = "browser_cache_ttl"; + $object->value = $ttl; + + array_push($this->configs, $object); + } + + public function setBrowserIntegrityCheck(bool $active) + { + $object = new \stdClass(); + $object->id = "browser_check"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setBypassCacheOnCookie(bool $value) + { + if (preg_match('/^([a-zA-Z0-9\.=|_*-]+)$/i', $value) < 1) { + throw new ConfigurationsException("Invalid cookie string."); + } + + $object = new \stdClass(); + $object->id = "bypass_cache_on_cookie"; + $object->value = $value; + + array_push($this->configs, $object); + } + + public function setCacheByDeviceType(bool $active) + { + $object = new \stdClass(); + $object->id = "cache_by_device_type"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setCacheKey(string $value) + { + $object = new \stdClass(); + $object->id = "cache_key"; + $object->value = $value; + + array_push($this->configs, $object); + } + + public function setCacheLevel(string $value) + { + if (!in_array($value, ["bypass", "basic", "simplified", "aggressive", "cache_everything"])) { + throw new ConfigurationsException("Invalid cache level"); + } + + $object = new \stdClass(); + $object->id = "cache_level"; + $object->value = $value; + + array_push($this->configs, $object); + } + + public function setCacheOnCookie(bool $value) + { + if (preg_match('/^([a-zA-Z0-9\.=|_*-]+)$/i', $value) < 1) { + throw new ConfigurationsException("Invalid cookie string."); + } + + $object = new \stdClass(); + $object->id = "cache_on_cookie"; + $object->value = $value; + + array_push($this->configs, $object); + } + + public function setDisableApps(bool $active) + { + $object = new \stdClass(); + $object->id = "disable_apps"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setDisablePerformance(bool $active) + { + $object = new \stdClass(); + $object->id = "disable_performance"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setDisableSecurity(bool $active) + { + $object = new \stdClass(); + $object->id = "disable_security"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setEdgeCacheTTL(int $value) + { + if ($value > 2419200) { + throw new ConfigurationsException("Edge Cache TTL too high."); + } + + $object = new \stdClass(); + $object->id = "edge_cache_ttl"; + $object->value = $value; + + array_push($this->configs, $object); + } + + public function setEmailObfuscation(bool $active) + { + $object = new \stdClass(); + $object->id = "disable_security"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setForwardingURL(int $statusCode, string $forwardingUrl) + { + if (in_array($statusCode, ['301', '302'])) { + throw new ConfigurationsException('Status Codes can only be 301 or 302.'); + } + + $object = new \stdClass(); + $object->id = "forwarding_url"; + $object->status_code = $statusCode; + $object->url = $forwardingUrl; + + array_push($this->configs, $object); + } + + public function setHostHeaderOverride(bool $active) + { + $object = new \stdClass(); + $object->id = "host_header_override"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setHotlinkProtection(bool $active) + { + $object = new \stdClass(); + $object->id = "hotlink_protection"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setIPGeoLocationHeader(bool $active) + { + $object = new \stdClass(); + $object->id = "ip_geolocation"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setMinification(bool $html, bool $css, bool $js) + { + $object = new \stdClass(); + $object->id = "minification"; + $object->html = $html === true ? "on" : "off"; + $object->css = $css === true ? "on" : "off"; + $object->js = $js === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setMirage(bool $active) + { + $object = new \stdClass(); + $object->id = "mirage"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setOriginErrorPagePassthru(bool $active) + { + $object = new \stdClass(); + $object->id = "origin_error_page_pass_thru"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setQueryStringSort(bool $active) + { + $object = new \stdClass(); + $object->id = "sort_query_string_for_cache"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setDisableRailgun(bool $active) + { + $object = new \stdClass(); + $object->id = "disable_railgun"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setResolveOverride(bool $value) + { + $object = new \stdClass(); + $object->id = "resolve_override"; + $object->value = $value; + + array_push($this->configs, $object); + } + + public function setRespectStrongEtag(bool $active) + { + $object = new \stdClass(); + $object->id = "respect_strong_etag"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setResponseBuffering(bool $active) + { + $object = new \stdClass(); + $object->id = "response_buffering"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setRocketLoader(string $value) + { + if (!in_array($value, ["off", "manual", "automatic"])) { + throw new ConfigurationsException('Rocket Loader can only be off, automatic, or manual.'); + } + + $object = new \stdClass(); + $object->id = "rocket_loader"; + $object->value = $value; + + array_push($this->configs, $object); + } + + public function setSecurityLevel(string $value) + { + if (!in_array($value, ["off", "essentially_off", "low", "medium", "high", "under_attack"])) { + throw new ConfigurationsException('Can only be set to off, essentially_off, low, medium, high or under_attack.'); + } + + $object = new \stdClass(); + $object->id = "security_level"; + $object->value = $value; + + array_push($this->configs, $object); + } + + public function setServerSideExcludes(bool $active) + { + $object = new \stdClass(); + $object->id = "server_side_exclude"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setSmartErrors(bool $active) + { + $object = new \stdClass(); + $object->id = "smart_errors"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setSSL(string $value) + { + if (!in_array($value, ["off", "flexible", "full", "strict", "origin_pull"])) { + throw new ConfigurationsException('Can only be set to off, flexible, full, strict, origin_pull.'); + } + + $object = new \stdClass(); + $object->id = "smart_errors"; + $object->value = $value; + + array_push($this->configs, $object); + } + + public function setTrueClientIpHeader(bool $active) + { + $object = new \stdClass(); + $object->id = "true_client_ip_header"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setWAF(bool $active) + { + $object = new \stdClass(); + $object->id = "waf"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setAutomatedHTTPSRewrites(bool $active) + { + $object = new \stdClass(); + $object->id = "automatic_https_rewrites"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function setOpportunisticEncryption(bool $active) + { + $object = new \stdClass(); + $object->id = "opportunistic_encryption"; + $object->value = $active === true ? "on" : "off"; + + array_push($this->configs, $object); + } + + public function getArray(): array + { + return $this->configs; + } +} \ No newline at end of file diff --git a/src/Configurations/PageRulesTargets.php b/src/Configurations/PageRulesTargets.php new file mode 100644 index 00000000..24c43a2c --- /dev/null +++ b/src/Configurations/PageRulesTargets.php @@ -0,0 +1,31 @@ +target = 'url'; + $target->constraint = new \stdClass(); + $target->constraint->operator = "matches"; + $target->constraint->value = $queryUrl; + + $this->targets = [$target]; + } + + public function getArray(): array + { + return $this->targets; + } +} \ No newline at end of file diff --git a/src/Endpoints/PageRules.php b/src/Endpoints/PageRules.php index 59074179..5e1a8976 100644 --- a/src/Endpoints/PageRules.php +++ b/src/Endpoints/PageRules.php @@ -6,10 +6,12 @@ * Time: 16:17 */ -namespace Cloudflare\API\Adapter; +namespace Cloudflare\API\Endpoints; -use Cloudflare\API\Endpoints\API; +use Cloudflare\API\Adapter\Adapter; +use Cloudflare\API\Configurations\PageRulesActions; +use Cloudflare\API\Configurations\PageRulesTargets; class PageRules implements API { @@ -19,4 +21,135 @@ public function __construct(Adapter $adapter) { $this->adapter = $adapter; } + + public function createPageRule( + string $zoneID, + PageRulesTargets $target, + PageRulesActions $actions, + bool $active = true, + int $priority = null + ): bool { + $options = [ + 'targets' => $target->getArray(), + 'actions' => $actions->getArray() + ]; + + if ($active !== null) { + $options['active'] = $active == true ? 'active' : 'disabled'; + } + + if ($priority !== null) { + $options['priority'] = $priority; + } + + + $query = $this->adapter->post('zones/' . $zoneID . '/pagerules', [], $options); + + $body = json_decode($query->getBody()); + + if (isset($body->result->id)) { + return true; + } + + return false; + } + + public function listPageRules( + string $zoneID, + string $status = null, + string $order = null, + string $direction = null, + string $match = null + ): \stdClass { + if (is_null($status) && !in_array($status, ['active', 'disabled'])) { + throw new EndpointException('Page Rules can only be listed by status of active or disabled.'); + } + + if (is_null($order) && !in_array($order, ['status', 'priority'])) { + throw new EndpointException('Page Rules can only be ordered by status or priority.'); + } + + if (is_null($direction) && !in_array($direction, ['asc', 'desc'])) { + throw new EndpointException('Direction of Page Rule ordering can only be asc or desc.'); + } + + if (is_null($match) && !in_array($match, ['all', 'any'])) { + throw new EndpointException('Match can only be any or all.'); + } + + $options = [ + 'status' => $status, + 'order' => $order, + 'direction' => $direction, + 'match' => $match + ]; + + $query = http_build_query($options); + + $user = $this->adapter->get('zones/' . $zoneID . '/pagerules?' . $query, []); + $body = json_decode($user->getBody()); + + $result = new \stdClass(); + $result->result = $body->result; + $result->result_info = $body->result_info; + + return $result; + } + + public function getPageRuleDetails(string $zoneID, string $ruleID): \stdClass + { + $user = $this->adapter->get('zones/' . $zoneID . '/pagerules/' . $ruleID, []); + $body = json_decode($user->getBody()); + return $body->result; + } + + public function updatePageRule( + string $zoneID, + PageRulesTargets $target = null, + PageRulesActions $actions = null, + bool $active = null, + int $priority = null + ): bool { + $options = []; + + if ($active !== null) { + $options['targets'] = $target->getArray(); + } + + if ($actions !== null) { + $options['actions'] = $actions->getArray(); + } + + if ($active !== null) { + $options['active'] = $active == true ? 'active' : 'disabled'; + } + + if ($priority !== null) { + $options['priority'] = $priority; + } + + + $query = $this->adapter->patch('zones/' . $zoneID . '/pagerules', [], $options); + + $body = json_decode($query->getBody()); + + if (isset($body->result->id)) { + return true; + } + + return false; + } + + public function deletePageRule(string $zoneID, string $ruleID): bool + { + $user = $this->adapter->delete('zones/' . $zoneID . '/pagerules/' . $ruleID, [], []); + + $body = json_decode($user->getBody()); + + if (isset($body->result->id)) { + return true; + } + + return false; + } } \ No newline at end of file diff --git a/tests/Configurations/PageRulesTargetTest.php b/tests/Configurations/PageRulesTargetTest.php new file mode 100644 index 00000000..4a9cb3d8 --- /dev/null +++ b/tests/Configurations/PageRulesTargetTest.php @@ -0,0 +1,21 @@ +getArray(); + + $this->assertEquals(1, sizeof($array)); + $this->assertEquals("junade.com/*", $array[0]->constraint->value); + $this->assertEquals("matches", $array[0]->constraint->operator); + } +} diff --git a/tests/Endpoints/PageRulesTest.php b/tests/Endpoints/PageRulesTest.php new file mode 100644 index 00000000..90547724 --- /dev/null +++ b/tests/Endpoints/PageRulesTest.php @@ -0,0 +1,261 @@ +setAlwaysOnline(true); + + $response = new GuzzleHttp\Psr7\Response(200, ['Content-Type' => 'application/json'], $stream); + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); + $mock->method('post')->willReturn($response); + + $mock->expects($this->once()) + ->method('post') + ->with($this->equalTo('zones/023e105f4ecef8ad9ca31a8372d0c353/pagerules'), $this->equalTo([]), + $this->equalTo([ + 'targets' => $target->getArray(), + 'actions' => $action->getArray(), + 'active' => 'active', + 'priority' => '1' + ]) + ); + + $pr = new \Cloudflare\API\Endpoints\PageRules($mock); + $result = $pr->createPageRule('023e105f4ecef8ad9ca31a8372d0c353', $target, $action, true, 1); + + $this->assertTrue($result); + } + + public function testListPageRules() + { + $stream = GuzzleHttp\Psr7\stream_for('{ + "success": true, + "errors": [ + {} + ], + "messages": [ + {} + ], + "result": [ + { + "id": "9a7806061c88ada191ed06f989cc3dac", + "targets": [ + { + "target": "url", + "constraint": { + "operator": "matches", + "value": "*example.com/images/*" + } + } + ], + "actions": [ + { + "id": "always_online", + "value": "on" + } + ], + "priority": 1, + "status": "active", + "modified_on": "2014-01-01T05:20:00.12345Z", + "created_on": "2014-01-01T05:20:00.12345Z" + } + ], + "result_info": { + "page": 1, + "per_page": 20, + "count": 1, + "total_count": 2000 + } +}'); + + $response = new GuzzleHttp\Psr7\Response(200, ['Content-Type' => 'application/json'], $stream); + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); + $mock->method('get')->willReturn($response); + + $mock->expects($this->once()) + ->method('get') + ->with($this->equalTo('zones/023e105f4ecef8ad9ca31a8372d0c353/pagerules?status=active&order=status&direction=desc&match=all'), $this->equalTo([]) + ); + + $pr = new \Cloudflare\API\Endpoints\PageRules($mock); + $pr->listPageRules('023e105f4ecef8ad9ca31a8372d0c353', 'active', 'status', 'desc', 'all'); + } + + public function testGetPageRuleDetails() + { + $stream = GuzzleHttp\Psr7\stream_for('{ + "success": true, + "errors": [ + {} + ], + "messages": [ + {} + ], + "result": { + "id": "9a7806061c88ada191ed06f989cc3dac", + "targets": [ + { + "target": "url", + "constraint": { + "operator": "matches", + "value": "*example.com/images/*" + } + } + ], + "actions": [ + { + "id": "always_online", + "value": "on" + } + ], + "priority": 1, + "status": "active", + "modified_on": "2014-01-01T05:20:00.12345Z", + "created_on": "2014-01-01T05:20:00.12345Z" + } +}'); + + $response = new GuzzleHttp\Psr7\Response(200, ['Content-Type' => 'application/json'], $stream); + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); + $mock->method('get')->willReturn($response); + + $mock->expects($this->once()) + ->method('get') + ->with($this->equalTo('zones/023e105f4ecef8ad9ca31a8372d0c353/pagerules/9a7806061c88ada191ed06f989cc3dac'), $this->equalTo([]) + ); + + $pr = new \Cloudflare\API\Endpoints\PageRules($mock); + $pr->getPageRuleDetails('023e105f4ecef8ad9ca31a8372d0c353', '9a7806061c88ada191ed06f989cc3dac'); + } + + public function testUpdatePageRule() + { + $stream = GuzzleHttp\Psr7\stream_for('{ + "success": true, + "errors": [ + {} + ], + "messages": [ + {} + ], + "result": { + "id": "9a7806061c88ada191ed06f989cc3dac", + "targets": [ + { + "target": "url", + "constraint": { + "operator": "matches", + "value": "*example.com/images/*" + } + } + ], + "actions": [ + { + "id": "always_online", + "value": "on" + } + ], + "priority": 1, + "status": "active", + "modified_on": "2014-01-01T05:20:00.12345Z", + "created_on": "2014-01-01T05:20:00.12345Z" + } +}'); + $target = new \Cloudflare\API\Configurations\PageRulesTargets('*example.com/images/*'); + $action = new \Cloudflare\API\Configurations\PageRulesActions(); + $action->setAlwaysOnline(true); + + $response = new GuzzleHttp\Psr7\Response(200, ['Content-Type' => 'application/json'], $stream); + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); + $mock->method('patch')->willReturn($response); + + $mock->expects($this->once()) + ->method('patch') + ->with($this->equalTo('zones/023e105f4ecef8ad9ca31a8372d0c353/pagerules'), $this->equalTo([]), + $this->equalTo([ + 'targets' => $target->getArray(), + 'actions' => $action->getArray(), + 'active' => 'active', + 'priority' => '1' + ]) + ); + + $pr = new \Cloudflare\API\Endpoints\PageRules($mock); + $result = $pr->updatePageRule('023e105f4ecef8ad9ca31a8372d0c353', $target, $action, true, 1); + + $this->assertTrue($result); + } + + public function testDeletePageRule() + { + $stream = GuzzleHttp\Psr7\stream_for('{ + "success": true, + "errors": [ + {} + ], + "messages": [ + {} + ], + "result": { + "id": "9a7806061c88ada191ed06f989cc3dac" + } +}'); + + $response = new GuzzleHttp\Psr7\Response(200, ['Content-Type' => 'application/json'], $stream); + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); + $mock->method('delete')->willReturn($response); + + $mock->expects($this->once()) + ->method('delete') + ->with($this->equalTo('zones/023e105f4ecef8ad9ca31a8372d0c353/pagerules/9a7806061c88ada191ed06f989cc3dac'), $this->equalTo([]), + $this->equalTo([]) + ); + + $pr = new \Cloudflare\API\Endpoints\PageRules($mock); + $result = $pr->deletePageRule('023e105f4ecef8ad9ca31a8372d0c353', '9a7806061c88ada191ed06f989cc3dac'); + + $this->assertTrue($result); + } +}