2013-08-17 13:16:48 +04:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ownCloud - App Framework
|
|
|
|
*
|
|
|
|
* @author Bernhard Posselt
|
2014-05-07 00:25:05 +04:00
|
|
|
* @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com>
|
2013-08-17 13:16:48 +04:00
|
|
|
*
|
|
|
|
* 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 OC\AppFramework\Middleware\Security;
|
|
|
|
|
2013-10-23 07:57:34 +04:00
|
|
|
use OC\AppFramework\Http;
|
2013-08-17 13:16:48 +04:00
|
|
|
use OC\AppFramework\Http\Request;
|
2014-05-06 18:29:19 +04:00
|
|
|
use OC\AppFramework\Utility\ControllerMethodReflector;
|
2014-04-20 18:12:46 +04:00
|
|
|
use OCP\AppFramework\Http\RedirectResponse;
|
2013-08-21 02:41:20 +04:00
|
|
|
use OCP\AppFramework\Http\JSONResponse;
|
2013-08-17 13:16:48 +04:00
|
|
|
|
|
|
|
|
|
|
|
class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase {
|
|
|
|
|
|
|
|
private $middleware;
|
|
|
|
private $controller;
|
|
|
|
private $secException;
|
|
|
|
private $secAjaxException;
|
|
|
|
private $request;
|
2014-05-06 18:29:19 +04:00
|
|
|
private $reader;
|
2013-08-17 13:16:48 +04:00
|
|
|
|
|
|
|
public function setUp() {
|
2013-10-07 13:25:50 +04:00
|
|
|
$api = $this->getMock('OC\AppFramework\DependencyInjection\DIContainer', array(), array('test'));
|
2013-10-11 12:07:57 +04:00
|
|
|
$this->controller = $this->getMock('OCP\AppFramework\Controller',
|
2013-08-17 13:16:48 +04:00
|
|
|
array(), array($api, new Request()));
|
2014-05-06 18:29:19 +04:00
|
|
|
$this->reader = new ControllerMethodReflector();
|
2013-08-17 13:16:48 +04:00
|
|
|
|
|
|
|
$this->request = new Request();
|
2014-05-06 18:29:19 +04:00
|
|
|
$this->middleware = new SecurityMiddleware($api, $this->request, $this->reader);
|
2013-08-17 13:16:48 +04:00
|
|
|
$this->secException = new SecurityException('hey', false);
|
|
|
|
$this->secAjaxException = new SecurityException('hey', true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function getAPI(){
|
2013-10-07 13:25:50 +04:00
|
|
|
return $this->getMock('OC\AppFramework\DependencyInjection\DIContainer',
|
2013-08-17 13:16:48 +04:00
|
|
|
array('isLoggedIn', 'passesCSRFCheck', 'isAdminUser',
|
2013-10-07 13:25:50 +04:00
|
|
|
'isSubAdminUser', 'getUserId'),
|
2013-08-17 13:16:48 +04:00
|
|
|
array('app'));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-19 12:31:54 +04:00
|
|
|
/**
|
|
|
|
* @param string $method
|
|
|
|
*/
|
2013-10-07 13:25:50 +04:00
|
|
|
private function checkNavEntry($method){
|
2013-08-17 13:16:48 +04:00
|
|
|
$api = $this->getAPI();
|
|
|
|
|
2013-10-07 13:25:50 +04:00
|
|
|
$serverMock = $this->getMock('\OC\Server', array());
|
|
|
|
$api->expects($this->any())->method('getServer')
|
|
|
|
->will($this->returnValue($serverMock));
|
2013-08-17 13:16:48 +04:00
|
|
|
|
2014-05-06 18:29:19 +04:00
|
|
|
$sec = new SecurityMiddleware($api, $this->request, $this->reader);
|
|
|
|
$this->reader->reflect('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', $method);
|
2013-08-17 13:16:48 +04:00
|
|
|
$sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', $method);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @PublicPage
|
|
|
|
* @NoCSRFRequired
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testSetNavigationEntry(){
|
2013-10-07 13:25:50 +04:00
|
|
|
$this->checkNavEntry('testSetNavigationEntry');
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-19 12:31:54 +04:00
|
|
|
/**
|
|
|
|
* @param string $method
|
|
|
|
* @param string $test
|
|
|
|
*/
|
2013-08-17 13:16:48 +04:00
|
|
|
private function ajaxExceptionStatus($method, $test, $status) {
|
|
|
|
$api = $this->getAPI();
|
|
|
|
$api->expects($this->any())
|
|
|
|
->method($test)
|
|
|
|
->will($this->returnValue(false));
|
|
|
|
|
2013-08-20 23:21:21 +04:00
|
|
|
// isAdminUser requires isLoggedIn call to return true
|
|
|
|
if ($test === 'isAdminUser') {
|
|
|
|
$api->expects($this->any())
|
|
|
|
->method('isLoggedIn')
|
|
|
|
->will($this->returnValue(true));
|
|
|
|
}
|
|
|
|
|
2014-05-06 18:29:19 +04:00
|
|
|
$sec = new SecurityMiddleware($api, $this->request, $this->reader);
|
2013-08-17 13:16:48 +04:00
|
|
|
|
|
|
|
try {
|
2014-05-06 18:29:19 +04:00
|
|
|
$controller = '\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest';
|
|
|
|
$this->reader->reflect($controller, $method);
|
|
|
|
$sec->beforeController($controller, $method);
|
2013-08-17 13:16:48 +04:00
|
|
|
} catch (SecurityException $ex){
|
|
|
|
$this->assertEquals($status, $ex->getCode());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testAjaxStatusLoggedInCheck() {
|
|
|
|
$this->ajaxExceptionStatus(
|
|
|
|
'testAjaxStatusLoggedInCheck',
|
|
|
|
'isLoggedIn',
|
|
|
|
Http::STATUS_UNAUTHORIZED
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @NoCSRFRequired
|
|
|
|
* @NoAdminRequired
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testAjaxNotAdminCheck() {
|
|
|
|
$this->ajaxExceptionStatus(
|
|
|
|
'testAjaxNotAdminCheck',
|
|
|
|
'isAdminUser',
|
|
|
|
Http::STATUS_FORBIDDEN
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @PublicPage
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testAjaxStatusCSRFCheck() {
|
|
|
|
$this->ajaxExceptionStatus(
|
|
|
|
'testAjaxStatusCSRFCheck',
|
|
|
|
'passesCSRFCheck',
|
|
|
|
Http::STATUS_PRECONDITION_FAILED
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @PublicPage
|
|
|
|
* @NoCSRFRequired
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testAjaxStatusAllGood() {
|
|
|
|
$this->ajaxExceptionStatus(
|
|
|
|
'testAjaxStatusAllGood',
|
|
|
|
'isLoggedIn',
|
|
|
|
0
|
|
|
|
);
|
|
|
|
$this->ajaxExceptionStatus(
|
|
|
|
'testAjaxStatusAllGood',
|
|
|
|
'isAdminUser',
|
|
|
|
0
|
|
|
|
);
|
|
|
|
$this->ajaxExceptionStatus(
|
|
|
|
'testAjaxStatusAllGood',
|
|
|
|
'isSubAdminUser',
|
|
|
|
0
|
|
|
|
);
|
|
|
|
$this->ajaxExceptionStatus(
|
|
|
|
'testAjaxStatusAllGood',
|
|
|
|
'passesCSRFCheck',
|
|
|
|
0
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-08-20 23:21:21 +04:00
|
|
|
|
2013-08-17 13:16:48 +04:00
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @PublicPage
|
|
|
|
* @NoCSRFRequired
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testNoChecks(){
|
|
|
|
$api = $this->getAPI();
|
|
|
|
$api->expects($this->never())
|
|
|
|
->method('passesCSRFCheck')
|
|
|
|
->will($this->returnValue(true));
|
|
|
|
$api->expects($this->never())
|
|
|
|
->method('isAdminUser')
|
|
|
|
->will($this->returnValue(true));
|
|
|
|
$api->expects($this->never())
|
|
|
|
->method('isLoggedIn')
|
|
|
|
->will($this->returnValue(true));
|
|
|
|
|
2014-05-06 18:29:19 +04:00
|
|
|
$sec = new SecurityMiddleware($api, $this->request, $this->reader);
|
|
|
|
$this->reader->reflect('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest',
|
|
|
|
'testNoChecks');
|
2013-08-17 13:16:48 +04:00
|
|
|
$sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest',
|
|
|
|
'testNoChecks');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-19 12:31:54 +04:00
|
|
|
/**
|
|
|
|
* @param string $method
|
|
|
|
* @param string $expects
|
|
|
|
*/
|
2013-08-17 13:16:48 +04:00
|
|
|
private function securityCheck($method, $expects, $shouldFail=false){
|
|
|
|
$api = $this->getAPI();
|
|
|
|
$api->expects($this->once())
|
|
|
|
->method($expects)
|
|
|
|
->will($this->returnValue(!$shouldFail));
|
|
|
|
|
2013-08-20 23:21:21 +04:00
|
|
|
// admin check requires login
|
|
|
|
if ($expects === 'isAdminUser') {
|
|
|
|
$api->expects($this->once())
|
|
|
|
->method('isLoggedIn')
|
|
|
|
->will($this->returnValue(true));
|
|
|
|
}
|
|
|
|
|
2014-05-06 18:29:19 +04:00
|
|
|
$sec = new SecurityMiddleware($api, $this->request, $this->reader);
|
2013-08-17 13:16:48 +04:00
|
|
|
|
|
|
|
if($shouldFail){
|
|
|
|
$this->setExpectedException('\OC\AppFramework\Middleware\Security\SecurityException');
|
2013-08-20 23:21:21 +04:00
|
|
|
} else {
|
|
|
|
$this->setExpectedException(null);
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
2014-05-06 18:29:19 +04:00
|
|
|
$this->reader->reflect('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', $method);
|
2013-08-17 13:16:48 +04:00
|
|
|
$sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', $method);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @PublicPage
|
2013-10-07 13:25:50 +04:00
|
|
|
* @expectedException \OC\AppFramework\Middleware\Security\SecurityException
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testCsrfCheck(){
|
2013-10-07 13:25:50 +04:00
|
|
|
$api = $this->getAPI();
|
|
|
|
$request = $this->getMock('OC\AppFramework\Http\Request', array('passesCSRFCheck'));
|
|
|
|
$request->expects($this->once())
|
|
|
|
->method('passesCSRFCheck')
|
|
|
|
->will($this->returnValue(false));
|
|
|
|
|
2014-05-06 18:29:19 +04:00
|
|
|
$sec = new SecurityMiddleware($api, $request, $this->reader);
|
|
|
|
$this->reader->reflect('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', 'testCsrfCheck');
|
2013-10-07 13:25:50 +04:00
|
|
|
$sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', 'testCsrfCheck');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @PublicPage
|
|
|
|
* @NoCSRFRequired
|
|
|
|
*/
|
|
|
|
public function testNoCsrfCheck(){
|
|
|
|
$api = $this->getAPI();
|
|
|
|
$request = $this->getMock('OC\AppFramework\Http\Request', array('passesCSRFCheck'));
|
|
|
|
$request->expects($this->never())
|
|
|
|
->method('passesCSRFCheck')
|
|
|
|
->will($this->returnValue(false));
|
|
|
|
|
2014-05-06 18:29:19 +04:00
|
|
|
$sec = new SecurityMiddleware($api, $request, $this->reader);
|
|
|
|
$this->reader->reflect('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', 'testNoCsrfCheck');
|
2013-10-07 13:25:50 +04:00
|
|
|
$sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', 'testNoCsrfCheck');
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @PublicPage
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testFailCsrfCheck(){
|
2013-10-07 13:25:50 +04:00
|
|
|
$api = $this->getAPI();
|
|
|
|
$request = $this->getMock('OC\AppFramework\Http\Request', array('passesCSRFCheck'));
|
|
|
|
$request->expects($this->once())
|
|
|
|
->method('passesCSRFCheck')
|
|
|
|
->will($this->returnValue(true));
|
|
|
|
|
2014-05-06 18:29:19 +04:00
|
|
|
$sec = new SecurityMiddleware($api, $request, $this->reader);
|
|
|
|
$this->reader->reflect('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', 'testFailCsrfCheck');
|
2013-10-07 13:25:50 +04:00
|
|
|
$sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', 'testFailCsrfCheck');
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @NoCSRFRequired
|
|
|
|
* @NoAdminRequired
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testLoggedInCheck(){
|
|
|
|
$this->securityCheck('testLoggedInCheck', 'isLoggedIn');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @NoCSRFRequired
|
|
|
|
* @NoAdminRequired
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testFailLoggedInCheck(){
|
|
|
|
$this->securityCheck('testFailLoggedInCheck', 'isLoggedIn', true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @NoCSRFRequired
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testIsAdminCheck(){
|
|
|
|
$this->securityCheck('testIsAdminCheck', 'isAdminUser');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @NoCSRFRequired
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testFailIsAdminCheck(){
|
|
|
|
$this->securityCheck('testFailIsAdminCheck', 'isAdminUser', true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testAfterExceptionNotCaughtThrowsItAgain(){
|
|
|
|
$ex = new \Exception();
|
|
|
|
$this->setExpectedException('\Exception');
|
|
|
|
$this->middleware->afterException($this->controller, 'test', $ex);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testAfterExceptionReturnsRedirect(){
|
2013-10-07 13:25:50 +04:00
|
|
|
$api = $this->getMock('OC\AppFramework\DependencyInjection\DIContainer', array(), array('test'));
|
|
|
|
$serverMock = $this->getMock('\OC\Server', array('getNavigationManager'));
|
|
|
|
$api->expects($this->once())->method('getServer')
|
|
|
|
->will($this->returnValue($serverMock));
|
|
|
|
|
2013-10-11 12:07:57 +04:00
|
|
|
$this->controller = $this->getMock('OCP\AppFramework\Controller',
|
2013-08-17 13:16:48 +04:00
|
|
|
array(), array($api, new Request()));
|
|
|
|
|
|
|
|
$this->request = new Request(
|
|
|
|
array('server' => array('HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8')));
|
2014-05-06 18:29:19 +04:00
|
|
|
$this->middleware = new SecurityMiddleware($api, $this->request, $this->reader);
|
2013-08-17 13:16:48 +04:00
|
|
|
$response = $this->middleware->afterException($this->controller, 'test',
|
|
|
|
$this->secException);
|
|
|
|
|
|
|
|
$this->assertTrue($response instanceof RedirectResponse);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testAfterAjaxExceptionReturnsJSONError(){
|
|
|
|
$response = $this->middleware->afterException($this->controller, 'test',
|
|
|
|
$this->secAjaxException);
|
|
|
|
|
|
|
|
$this->assertTrue($response instanceof JSONResponse);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|