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
|
|
|
|
|
|
|
|
2014-11-11 01:30:38 +03:00
|
|
|
class SecurityMiddlewareTest extends \Test\TestCase {
|
2013-08-17 13:16:48 +04:00
|
|
|
|
|
|
|
private $middleware;
|
|
|
|
private $controller;
|
|
|
|
private $secException;
|
|
|
|
private $secAjaxException;
|
|
|
|
private $request;
|
2014-05-06 18:29:19 +04:00
|
|
|
private $reader;
|
2014-05-28 04:12:01 +04:00
|
|
|
private $logger;
|
|
|
|
private $navigationManager;
|
|
|
|
private $urlGenerator;
|
2013-08-17 13:16:48 +04:00
|
|
|
|
2014-11-11 01:30:38 +03:00
|
|
|
protected function setUp() {
|
|
|
|
parent::setUp();
|
|
|
|
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->controller = $this->getMockBuilder('OCP\AppFramework\Controller')
|
|
|
|
->disableOriginalConstructor()
|
|
|
|
->getMock();
|
2014-05-06 18:29:19 +04:00
|
|
|
$this->reader = new ControllerMethodReflector();
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->logger = $this->getMockBuilder(
|
|
|
|
'OCP\ILogger')
|
|
|
|
->disableOriginalConstructor()
|
|
|
|
->getMock();
|
|
|
|
$this->navigationManager = $this->getMockBuilder(
|
|
|
|
'OCP\INavigationManager')
|
|
|
|
->disableOriginalConstructor()
|
|
|
|
->getMock();
|
|
|
|
$this->urlGenerator = $this->getMockBuilder(
|
|
|
|
'OCP\IURLGenerator')
|
|
|
|
->disableOriginalConstructor()
|
|
|
|
->getMock();
|
|
|
|
$this->request = $this->getMockBuilder(
|
|
|
|
'OCP\IRequest')
|
|
|
|
->disableOriginalConstructor()
|
|
|
|
->getMock();
|
|
|
|
$this->middleware = $this->getMiddleware(true, true);
|
2013-08-17 13:16:48 +04:00
|
|
|
$this->secException = new SecurityException('hey', false);
|
|
|
|
$this->secAjaxException = new SecurityException('hey', true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-28 04:12:01 +04:00
|
|
|
private function getMiddleware($isLoggedIn, $isAdminUser){
|
|
|
|
return new SecurityMiddleware(
|
|
|
|
$this->request,
|
|
|
|
$this->reader,
|
|
|
|
$this->navigationManager,
|
|
|
|
$this->urlGenerator,
|
|
|
|
$this->logger,
|
2014-11-14 19:20:51 +03:00
|
|
|
'files',
|
2014-05-28 04:12:01 +04:00
|
|
|
$isLoggedIn,
|
|
|
|
$isAdminUser
|
|
|
|
);
|
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 testSetNavigationEntry(){
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->navigationManager->expects($this->once())
|
|
|
|
->method('setActiveEntry')
|
2014-11-14 19:20:51 +03:00
|
|
|
->with($this->equalTo('files'));
|
2014-05-28 04:12:01 +04:00
|
|
|
|
|
|
|
$this->reader->reflect(__CLASS__, __FUNCTION__);
|
|
|
|
$this->middleware->beforeController(__CLASS__, __FUNCTION__);
|
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) {
|
2014-05-28 04:12:01 +04:00
|
|
|
$isLoggedIn = false;
|
|
|
|
$isAdminUser = false;
|
2013-08-17 13:16:48 +04:00
|
|
|
|
2013-08-20 23:21:21 +04:00
|
|
|
// isAdminUser requires isLoggedIn call to return true
|
|
|
|
if ($test === 'isAdminUser') {
|
2014-05-28 04:12:01 +04:00
|
|
|
$isLoggedIn = true;
|
2013-08-20 23:21:21 +04:00
|
|
|
}
|
|
|
|
|
2014-05-28 04:12:01 +04:00
|
|
|
$sec = $this->getMiddleware($isLoggedIn, $isAdminUser);
|
2013-08-17 13:16:48 +04:00
|
|
|
|
|
|
|
try {
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->reader->reflect(__CLASS__, $method);
|
|
|
|
$sec->beforeController(__CLASS__, $method);
|
2013-08-17 13:16:48 +04:00
|
|
|
} catch (SecurityException $ex){
|
|
|
|
$this->assertEquals($status, $ex->getCode());
|
|
|
|
}
|
2014-05-28 17:23:57 +04:00
|
|
|
|
|
|
|
// add assertion if everything should work fine otherwise phpunit will
|
|
|
|
// complain
|
|
|
|
if ($status === 0) {
|
|
|
|
$this->assertTrue(true);
|
|
|
|
}
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
public function testAjaxStatusLoggedInCheck() {
|
|
|
|
$this->ajaxExceptionStatus(
|
2014-05-28 17:23:57 +04:00
|
|
|
__FUNCTION__,
|
2013-08-17 13:16:48 +04:00
|
|
|
'isLoggedIn',
|
|
|
|
Http::STATUS_UNAUTHORIZED
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @NoCSRFRequired
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testAjaxNotAdminCheck() {
|
|
|
|
$this->ajaxExceptionStatus(
|
2014-05-28 17:23:57 +04:00
|
|
|
__FUNCTION__,
|
2013-08-17 13:16:48 +04:00
|
|
|
'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(
|
2014-05-28 17:23:57 +04:00
|
|
|
__FUNCTION__,
|
2013-08-17 13:16:48 +04:00
|
|
|
'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(
|
2014-05-28 17:23:57 +04:00
|
|
|
__FUNCTION__,
|
2013-08-17 13:16:48 +04:00
|
|
|
'isLoggedIn',
|
|
|
|
0
|
|
|
|
);
|
|
|
|
$this->ajaxExceptionStatus(
|
2014-05-28 17:23:57 +04:00
|
|
|
__FUNCTION__,
|
2013-08-17 13:16:48 +04:00
|
|
|
'isAdminUser',
|
|
|
|
0
|
|
|
|
);
|
|
|
|
$this->ajaxExceptionStatus(
|
2014-05-28 17:23:57 +04:00
|
|
|
__FUNCTION__,
|
2013-08-17 13:16:48 +04:00
|
|
|
'isSubAdminUser',
|
|
|
|
0
|
|
|
|
);
|
|
|
|
$this->ajaxExceptionStatus(
|
2014-05-28 17:23:57 +04:00
|
|
|
__FUNCTION__,
|
2013-08-17 13:16:48 +04:00
|
|
|
'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(){
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->request->expects($this->never())
|
2013-08-17 13:16:48 +04:00
|
|
|
->method('passesCSRFCheck')
|
2014-05-28 04:12:01 +04:00
|
|
|
->will($this->returnValue(false));
|
|
|
|
|
|
|
|
$sec = $this->getMiddleware(false, false);
|
|
|
|
|
|
|
|
$this->reader->reflect(__CLASS__, __FUNCTION__);
|
|
|
|
$sec->beforeController(__CLASS__, __FUNCTION__);
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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){
|
2013-08-20 23:21:21 +04:00
|
|
|
// admin check requires login
|
|
|
|
if ($expects === 'isAdminUser') {
|
2014-05-28 04:12:01 +04:00
|
|
|
$isLoggedIn = true;
|
|
|
|
$isAdminUser = !$shouldFail;
|
|
|
|
} else {
|
|
|
|
$isLoggedIn = !$shouldFail;
|
|
|
|
$isAdminUser = false;
|
2013-08-20 23:21:21 +04:00
|
|
|
}
|
|
|
|
|
2014-05-28 04:12:01 +04:00
|
|
|
$sec = $this->getMiddleware($isLoggedIn, $isAdminUser);
|
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 {
|
2014-05-28 17:23:57 +04:00
|
|
|
$this->assertTrue(true);
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->reader->reflect(__CLASS__, $method);
|
|
|
|
$sec->beforeController(__CLASS__, $method);
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
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(){
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->request->expects($this->once())
|
2013-10-07 13:25:50 +04:00
|
|
|
->method('passesCSRFCheck')
|
|
|
|
->will($this->returnValue(false));
|
|
|
|
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->reader->reflect(__CLASS__, __FUNCTION__);
|
|
|
|
$this->middleware->beforeController(__CLASS__, __FUNCTION__);
|
2013-10-07 13:25:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @PublicPage
|
|
|
|
* @NoCSRFRequired
|
|
|
|
*/
|
|
|
|
public function testNoCsrfCheck(){
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->request->expects($this->never())
|
2013-10-07 13:25:50 +04:00
|
|
|
->method('passesCSRFCheck')
|
|
|
|
->will($this->returnValue(false));
|
|
|
|
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->reader->reflect(__CLASS__, __FUNCTION__);
|
|
|
|
$this->middleware->beforeController(__CLASS__, __FUNCTION__);
|
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(){
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->request->expects($this->once())
|
2013-10-07 13:25:50 +04:00
|
|
|
->method('passesCSRFCheck')
|
|
|
|
->will($this->returnValue(true));
|
|
|
|
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->reader->reflect(__CLASS__, __FUNCTION__);
|
|
|
|
$this->middleware->beforeController(__CLASS__, __FUNCTION__);
|
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(){
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->securityCheck(__FUNCTION__, 'isLoggedIn');
|
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 testFailLoggedInCheck(){
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->securityCheck(__FUNCTION__, 'isLoggedIn', true);
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @NoCSRFRequired
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testIsAdminCheck(){
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->securityCheck(__FUNCTION__, 'isAdminUser');
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2013-08-20 23:21:21 +04:00
|
|
|
* @NoCSRFRequired
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function testFailIsAdminCheck(){
|
2014-05-28 04:12:01 +04:00
|
|
|
$this->securityCheck(__FUNCTION__, 'isAdminUser', true);
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testAfterExceptionNotCaughtThrowsItAgain(){
|
|
|
|
$ex = new \Exception();
|
|
|
|
$this->setExpectedException('\Exception');
|
|
|
|
$this->middleware->afterException($this->controller, 'test', $ex);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testAfterExceptionReturnsRedirect(){
|
|
|
|
$this->request = new Request(
|
2015-02-09 13:41:48 +03:00
|
|
|
[
|
|
|
|
'server' =>
|
|
|
|
[
|
|
|
|
'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
|
|
'REQUEST_URI' => 'owncloud/index.php/apps/specialapp'
|
|
|
|
]
|
|
|
|
],
|
2015-02-10 15:02:48 +03:00
|
|
|
$this->getMock('\OCP\Security\ISecureRandom'),
|
2015-08-13 08:36:42 +03:00
|
|
|
$this->getMock('\OCP\Security\ICrypto'),
|
2015-02-10 15:02:48 +03:00
|
|
|
$this->getMock('\OCP\IConfig')
|
2014-05-28 04:12:01 +04:00
|
|
|
);
|
|
|
|
$this->middleware = $this->getMiddleware(true, true);
|
2013-08-17 13:16:48 +04:00
|
|
|
$response = $this->middleware->afterException($this->controller, 'test',
|
|
|
|
$this->secException);
|
|
|
|
|
|
|
|
$this->assertTrue($response instanceof RedirectResponse);
|
2014-07-02 03:06:58 +04:00
|
|
|
$this->assertEquals('?redirect_url=owncloud%2Findex.php%2Fapps%2Fspecialapp', $response->getRedirectURL());
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function testAfterAjaxExceptionReturnsJSONError(){
|
|
|
|
$response = $this->middleware->afterException($this->controller, 'test',
|
|
|
|
$this->secAjaxException);
|
|
|
|
|
|
|
|
$this->assertTrue($response instanceof JSONResponse);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|