Merge pull request #8504 from owncloud/cors-middleware
Add cors middlewareremotes/origin/ldap_group_count
commit
a252f59cd4
@ -0,0 +1,72 @@ |
||||
<?php |
||||
/** |
||||
* ownCloud - App Framework |
||||
* |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. See the COPYING file. |
||||
* |
||||
* @author Bernhard Posselt <dev@bernhard-posselt.com> |
||||
* @copyright Bernhard Posselt 2014 |
||||
*/ |
||||
|
||||
namespace OC\AppFramework\Middleware\Security; |
||||
|
||||
use OC\AppFramework\Utility\MethodAnnotationReader; |
||||
use OCP\IRequest; |
||||
use OCP\AppFramework\Http\Response; |
||||
use OCP\AppFramework\Middleware; |
||||
|
||||
/** |
||||
* This middleware sets the correct CORS headers on a response if the |
||||
* controller has the @CORS annotation. This is needed for webapps that want |
||||
* to access an API and dont run on the same domain, see |
||||
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS |
||||
*/ |
||||
class CORSMiddleware extends Middleware { |
||||
|
||||
private $request; |
||||
|
||||
/** |
||||
* @param IRequest $request |
||||
*/ |
||||
public function __construct(IRequest $request) { |
||||
$this->request = $request; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* This is being run after a successful controllermethod call and allows |
||||
* the manipulation of a Response object. The middleware is run in reverse order |
||||
* |
||||
* @param Controller $controller the controller that is being called |
||||
* @param string $methodName the name of the method that will be called on |
||||
* the controller |
||||
* @param Response $response the generated response from the controller |
||||
* @return Response a Response object |
||||
*/ |
||||
public function afterController($controller, $methodName, Response $response){ |
||||
// only react if its a CORS request and if the request sends origin and |
||||
$reflector = new MethodAnnotationReader($controller, $methodName); |
||||
|
||||
if(isset($this->request->server['HTTP_ORIGIN']) && |
||||
$reflector->hasAnnotation('CORS')) { |
||||
|
||||
// allow credentials headers must not be true or CSRF is possible |
||||
// otherwise |
||||
foreach($response->getHeaders() as $header => $value ) { |
||||
if(strtolower($header) === 'access-control-allow-credentials' && |
||||
strtolower(trim($value)) === 'true') { |
||||
$msg = 'Access-Control-Allow-Credentials must not be '. |
||||
'set to true in order to prevent CSRF'; |
||||
throw new SecurityException($msg); |
||||
} |
||||
} |
||||
|
||||
$origin = $this->request->server['HTTP_ORIGIN']; |
||||
$response->addHeader('Access-Control-Allow-Origin', $origin); |
||||
} |
||||
return $response; |
||||
} |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,93 @@ |
||||
<?php |
||||
/** |
||||
* ownCloud - App Framework |
||||
* |
||||
* @author Bernhard Posselt |
||||
* @copyright 2012 Bernhard Posselt nukeawhale@gmail.com |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
* License as published by the Free Software Foundation; either |
||||
* version 3 of the License, or any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public |
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
/** |
||||
* Public interface of ownCloud for apps to use. |
||||
* AppFramework\Controller class |
||||
*/ |
||||
|
||||
namespace OCP\AppFramework; |
||||
|
||||
use OCP\AppFramework\Http\Response; |
||||
use OCP\IRequest; |
||||
|
||||
|
||||
/** |
||||
* Base class to inherit your controllers from that are used for RESTful APIs |
||||
*/ |
||||
abstract class ApiController extends Controller { |
||||
|
||||
private $corsMethods; |
||||
private $corsAllowedHeaders; |
||||
private $corsMaxAge; |
||||
|
||||
/** |
||||
* constructor of the controller |
||||
* @param string $appName the name of the app |
||||
* @param IRequest $request an instance of the request |
||||
* @param string $corsMethods: comma seperated string of HTTP verbs which |
||||
* should be allowed for websites or webapps when calling your API, defaults to |
||||
* 'PUT, POST, GET, DELETE, PATCH' |
||||
* @param string $corsAllowedHeaders: comma seperated string of HTTP headers |
||||
* which should be allowed for websites or webapps when calling your API, |
||||
* defaults to 'Authorization, Content-Type, Accept' |
||||
* @param int $corsMaxAge number in seconds how long a preflighted OPTIONS |
||||
* request should be cached, defaults to 1728000 seconds |
||||
*/ |
||||
public function __construct($appName, |
||||
IRequest $request, |
||||
$corsMethods='PUT, POST, GET, DELETE, PATCH', |
||||
$corsAllowedHeaders='Authorization, Content-Type, Accept', |
||||
$corsMaxAge=1728000){ |
||||
parent::__construct($appName, $request); |
||||
$this->corsMethods = $corsMethods; |
||||
$this->corsAllowedHeaders = $corsAllowedHeaders; |
||||
$this->corsMaxAge = $corsMaxAge; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* This method implements a preflighted cors response for you that you can |
||||
* link to for the options request |
||||
* |
||||
* @NoAdminRequired |
||||
* @NoCSRFRequired |
||||
* @PublicPage |
||||
*/ |
||||
public function preflightedCors() { |
||||
if(isset($this->request->server['HTTP_ORIGIN'])) { |
||||
$origin = $this->request->server['HTTP_ORIGIN']; |
||||
} else { |
||||
$origin = '*'; |
||||
} |
||||
|
||||
$response = new Response(); |
||||
$response->addHeader('Access-Control-Allow-Origin', $origin); |
||||
$response->addHeader('Access-Control-Allow-Methods', $this->corsMethods); |
||||
$response->addHeader('Access-Control-Max-Age', $this->corsMaxAge); |
||||
$response->addHeader('Access-Control-Allow-Headers', $this->corsAllowedHeaders); |
||||
$response->addHeader('Access-Control-Allow-Credentials', 'false'); |
||||
return $response; |
||||
} |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,55 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* ownCloud - App Framework |
||||
* |
||||
* @author Bernhard Posselt |
||||
* @copyright 2012 Bernhard Posselt nukeawhale@gmail.com |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
* License as published by the Free Software Foundation; either |
||||
* version 3 of the License, or any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public |
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
|
||||
namespace OCP\AppFramework; |
||||
|
||||
use OC\AppFramework\Http\Request; |
||||
use OCP\AppFramework\Http\TemplateResponse; |
||||
|
||||
|
||||
class ChildApiController extends ApiController {}; |
||||
|
||||
|
||||
class ApiControllerTest extends \PHPUnit_Framework_TestCase { |
||||
|
||||
|
||||
public function testCors() { |
||||
$request = new Request( |
||||
array('server' => array('HTTP_ORIGIN' => 'test')) |
||||
); |
||||
$this->controller = new ChildApiController('app', $request, 'verbs', |
||||
'headers', 100); |
||||
|
||||
$response = $this->controller->preflightedCors(); |
||||
|
||||
$headers = $response->getHeaders(); |
||||
|
||||
$this->assertEquals('test', $headers['Access-Control-Allow-Origin']); |
||||
$this->assertEquals('verbs', $headers['Access-Control-Allow-Methods']); |
||||
$this->assertEquals('headers', $headers['Access-Control-Allow-Headers']); |
||||
$this->assertEquals('false', $headers['Access-Control-Allow-Credentials']); |
||||
$this->assertEquals(100, $headers['Access-Control-Max-Age']); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,77 @@ |
||||
<?php |
||||
/** |
||||
* ownCloud - App Framework |
||||
* |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. See the COPYING file. |
||||
* |
||||
* @author Bernhard Posselt <dev@bernhard-posselt.com> |
||||
* @copyright Bernhard Posselt 2014 |
||||
*/ |
||||
|
||||
|
||||
namespace OC\AppFramework\Middleware\Security; |
||||
|
||||
use OC\AppFramework\Http\Request; |
||||
use OCP\AppFramework\Http\Response; |
||||
|
||||
|
||||
class CORSMiddlewareTest extends \PHPUnit_Framework_TestCase { |
||||
|
||||
/** |
||||
* @CORS |
||||
*/ |
||||
public function testSetCORSAPIHeader() { |
||||
$request = new Request( |
||||
array('server' => array('HTTP_ORIGIN' => 'test')) |
||||
); |
||||
|
||||
$middleware = new CORSMiddleware($request); |
||||
$response = $middleware->afterController($this, __FUNCTION__, new Response()); |
||||
$headers = $response->getHeaders(); |
||||
|
||||
$this->assertEquals('test', $headers['Access-Control-Allow-Origin']); |
||||
} |
||||
|
||||
|
||||
public function testNoAnnotationNoCORSHEADER() { |
||||
$request = new Request( |
||||
array('server' => array('HTTP_ORIGIN' => 'test')) |
||||
); |
||||
$middleware = new CORSMiddleware($request); |
||||
|
||||
$response = $middleware->afterController($this, __FUNCTION__, new Response()); |
||||
$headers = $response->getHeaders(); |
||||
$this->assertFalse(array_key_exists('Access-Control-Allow-Origin', $headers)); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* @CORS |
||||
*/ |
||||
public function testNoOriginHeaderNoCORSHEADER() { |
||||
$request = new Request(); |
||||
|
||||
$middleware = new CORSMiddleware($request); |
||||
$response = $middleware->afterController($this, __FUNCTION__, new Response()); |
||||
$headers = $response->getHeaders(); |
||||
$this->assertFalse(array_key_exists('Access-Control-Allow-Origin', $headers)); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* @CORS |
||||
* @expectedException \OC\AppFramework\Middleware\Security\SecurityException |
||||
*/ |
||||
public function testCorsIgnoredIfWithCredentialsHeaderPresent() { |
||||
$request = new Request( |
||||
array('server' => array('HTTP_ORIGIN' => 'test')) |
||||
); |
||||
$middleware = new CORSMiddleware($request); |
||||
|
||||
$response = new Response(); |
||||
$response->addHeader('AcCess-control-Allow-Credentials ', 'TRUE'); |
||||
$response = $middleware->afterController($this, __FUNCTION__, $response); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue