Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added support to cross origin requests

  • Loading branch information...
commit ec7196013c72b09dae46aaa69f6fc0f50cb5f351 1 parent 75dd2ff
@jrbasso jrbasso authored
View
67 lib/Cake/Network/CakeResponse.php
@@ -1241,6 +1241,73 @@ public function cookie($options = null) {
}
/**
+ * Setup access for origin and methods on cross origin requests
+ *
+ * This method allow multiple ways to setup the domains, see the examples
+ *
+ * ### Full URI
+ * e.g `cors($request, 'http://www.cakephp.org');`
+ *
+ * ### URI with wildcard
+ * e.g `cors($request, 'http://*.cakephp.org');`
+ *
+ * ### Ignoring the requested protocol
+ * e.g `cors($request, 'www.cakephp.org');`
+ *
+ * ### Any URI
+ * e.g `cors($request, '*');`
+ *
+ * ### Any of URI
+ * e.g `cors($request, array('http://www.cakephp.org', '*.google.com', 'https://myproject.github.io'));`
+ *
+ * @param CakeRequest $request Request object
+ * @param string|array $allowedDomains List of allowed domains, see method description for more details
+ * @param string|array $allowedMethods List of HTTP verbs allowed
+ * @return void
+ */
+ public function cors(CakeRequest $request, $allowedDomains, $allowedMethods = array()) {
+ $origin = $request::header('Origin');
+ if (!$origin) {
+ return;
+ }
+
+ $allowedDomains = $this->normalizeCorsDomains((array)$allowedDomains, $request->is('ssl'));
+ foreach ($allowedDomains as $domain) {
+ if (!preg_match($domain['preg'], $origin)) {
+ continue;
+ }
+ $this->header('Access-Control-Allow-Origin', $domain['original'] === '*' ? '*' : $origin);
+ $allowedMethods && $this->header('Access-Control-Allow-Methods', implode(', ', (array)$allowedMethods));
+ break;
+ }
+ }
+
+/**
+ * Normalize the origin to regular expressions and put in an array format
+ *
+ * @param array $domains
+ * @param boolean $requestIsSSL
+ * @return array
+ */
+ protected function normalizeCorsDomains($domains, $requestIsSSL = false) {
+ $result = array();
+ foreach ($domains as $domain) {
+ if ($domain === '*') {
+ $result[] = array('preg' => '@.@', 'original' => '*');
+ continue;
+ }
+
+ $original = $preg = $domain;
+ if (strpos($domain, '://') === false) {
+ $preg = ($requestIsSSL ? 'https://' : 'http://') . $domain;
+ }
+ $preg = '@' . str_replace('*', '.*', $domain) . '@';
+ $result[] = compact('original', 'preg');
+ }
+ return $result;
+ }
+
+/**
* Setup for display or download the given file.
*
* If $_SERVER['HTTP_RANGE'] is set a slice of the file will be
View
73 lib/Cake/Test/Case/Network/CakeResponseTest.php
@@ -1062,6 +1062,79 @@ public function testCookieSettings() {
}
/**
+ * Test CORS
+ *
+ * @dataProvider corsData
+ * @param CakeRequest $request
+ * @param string|array $domains
+ * @param string|array $methods
+ * @param string|boolean $expectedOrigin
+ * @param string|boolean $expectedMethods
+ * @return void
+ */
+ public function testCors($request, $domains, $methods, $expectedOrigin, $expectedMethods) {
+ $response = $this->getMock('CakeResponse', array('header'));
+ if ($expectedOrigin === false) {
+ $response->expects($this->never())
+ ->method('header');
+ } elseif ($expectedMethods === false) {
+ $response->expects($this->once())
+ ->method('header')
+ ->with('Access-Control-Allow-Origin', $expectedOrigin);
+ } else {
+ $response->expects($this->at(0))
+ ->method('header')
+ ->with('Access-Control-Allow-Origin', $expectedOrigin);
+ $response->expects($this->at(1))
+ ->method('header')
+ ->with('Access-Control-Allow-Methods', $expectedMethods);
+ }
+
+ $response->cors($request, $domains, $methods);
+ }
+
+/**
+ * Feed for testCors
+ *
+ * @return array
+ */
+ public function corsData() {
+ $fooRequest = $this->getMock('CakeRequest', array('header'));
+ $fooRequest::staticExpects($this->any())
+ ->method('header')
+ ->with('Origin')
+ ->will($this->returnValue('http://www.foo.com'));
+
+ $secureRequest = $this->getMock('CakeRequest', array('header', 'is'));
+ $secureRequest::staticExpects($this->any())
+ ->method('header')
+ ->with('Origin')
+ ->will($this->returnValue('https://www.bar.com'));
+ $secureRequest->expects($this->any())
+ ->method('is')
+ ->with('ssl')
+ ->will($this->returnValue(true));
+
+ return array(
+ array($fooRequest, '*', '', '*', false),
+ array($fooRequest, 'www.foo.com', '', 'http://www.foo.com', false),
+ array($fooRequest, '*.foo.com', '', 'http://www.foo.com', false),
+ array($fooRequest, 'http://*.foo.com', '', 'http://www.foo.com', false),
+ array($fooRequest, 'https://www.foo.com', '', false, false),
+ array($fooRequest, 'https://*.foo.com', '', false, false),
+ array($fooRequest, array('*.bar.com', '*.foo.com'), '', 'http://www.foo.com', false),
+
+ array($secureRequest, 'www.bar.com', '', 'https://www.bar.com', false),
+ array($secureRequest, 'http://www.bar.com', '', false, false),
+ array($secureRequest, '*.bar.com', '', 'https://www.bar.com', false),
+
+ array($fooRequest, '*', 'GET', '*', 'GET'),
+ array($fooRequest, '*.foo.com', 'GET', 'http://www.foo.com', 'GET'),
+ array($fooRequest, '*.foo.com', array('GET', 'POST'), 'http://www.foo.com', 'GET, POST'),
+ );
+ }
+
+/**
* testFileNotFound
*
* @expectedException NotFoundException
Please sign in to comment.
Something went wrong with that request. Please try again.