SecurityMiddlewareTest.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. <?php
  2. /**
  3. * @author Bernhard Posselt <dev@bernhard-posselt.com>
  4. * @author Lukas Reschke <lukas@owncloud.com>
  5. *
  6. * @copyright Copyright (c) 2015, ownCloud, Inc.
  7. * @license AGPL-3.0
  8. *
  9. * This code is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License, version 3,
  11. * as published by the Free Software Foundation.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Affero General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public License, version 3,
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>
  20. *
  21. */
  22. namespace Test\AppFramework\Middleware\Security;
  23. use OC\AppFramework\Http;
  24. use OC\AppFramework\Http\Request;
  25. use OC\AppFramework\Middleware\Security\Exceptions\AppNotEnabledException;
  26. use OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException;
  27. use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException;
  28. use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException;
  29. use OC\AppFramework\Middleware\Security\Exceptions\SecurityException;
  30. use OC\Appframework\Middleware\Security\Exceptions\StrictCookieMissingException;
  31. use OC\AppFramework\Middleware\Security\SecurityMiddleware;
  32. use OC\AppFramework\Utility\ControllerMethodReflector;
  33. use OC\Settings\AuthorizedGroupMapper;
  34. use OCP\App\IAppManager;
  35. use OCP\AppFramework\Http\JSONResponse;
  36. use OCP\AppFramework\Http\RedirectResponse;
  37. use OCP\AppFramework\Http\TemplateResponse;
  38. use OCP\IConfig;
  39. use OCP\IL10N;
  40. use OCP\INavigationManager;
  41. use OCP\IRequest;
  42. use OCP\IRequestId;
  43. use OCP\IURLGenerator;
  44. use OCP\IUserSession;
  45. use Psr\Log\LoggerInterface;
  46. use Test\AppFramework\Middleware\Security\Mock\NormalController;
  47. use Test\AppFramework\Middleware\Security\Mock\OCSController;
  48. use Test\AppFramework\Middleware\Security\Mock\SecurityMiddlewareController;
  49. class SecurityMiddlewareTest extends \Test\TestCase {
  50. /** @var SecurityMiddleware|\PHPUnit\Framework\MockObject\MockObject */
  51. private $middleware;
  52. /** @var SecurityMiddlewareController */
  53. private $controller;
  54. /** @var SecurityException */
  55. private $secException;
  56. /** @var SecurityException */
  57. private $secAjaxException;
  58. /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */
  59. private $request;
  60. /** @var ControllerMethodReflector */
  61. private $reader;
  62. /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */
  63. private $logger;
  64. /** @var INavigationManager|\PHPUnit\Framework\MockObject\MockObject */
  65. private $navigationManager;
  66. /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
  67. private $urlGenerator;
  68. /** @var IAppManager|\PHPUnit\Framework\MockObject\MockObject */
  69. private $appManager;
  70. /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
  71. private $l10n;
  72. /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
  73. private $userSession;
  74. /** @var AuthorizedGroupMapper|\PHPUnit\Framework\MockObject\MockObject */
  75. private $authorizedGroupMapper;
  76. protected function setUp(): void {
  77. parent::setUp();
  78. $this->authorizedGroupMapper = $this->createMock(AuthorizedGroupMapper::class);
  79. $this->userSession = $this->createMock(IUserSession::class);
  80. $this->request = $this->createMock(IRequest::class);
  81. $this->controller = new SecurityMiddlewareController(
  82. 'test',
  83. $this->request
  84. );
  85. $this->reader = new ControllerMethodReflector();
  86. $this->logger = $this->createMock(LoggerInterface::class);
  87. $this->navigationManager = $this->createMock(INavigationManager::class);
  88. $this->urlGenerator = $this->createMock(IURLGenerator::class);
  89. $this->l10n = $this->createMock(IL10N::class);
  90. $this->middleware = $this->getMiddleware(true, true, false);
  91. $this->secException = new SecurityException('hey', false);
  92. $this->secAjaxException = new SecurityException('hey', true);
  93. }
  94. private function getMiddleware(bool $isLoggedIn, bool $isAdminUser, bool $isSubAdmin, bool $isAppEnabledForUser = true): SecurityMiddleware {
  95. $this->appManager = $this->createMock(IAppManager::class);
  96. $this->appManager->expects($this->any())
  97. ->method('isEnabledForUser')
  98. ->willReturn($isAppEnabledForUser);
  99. return new SecurityMiddleware(
  100. $this->request,
  101. $this->reader,
  102. $this->navigationManager,
  103. $this->urlGenerator,
  104. $this->logger,
  105. 'files',
  106. $isLoggedIn,
  107. $isAdminUser,
  108. $isSubAdmin,
  109. $this->appManager,
  110. $this->l10n,
  111. $this->authorizedGroupMapper,
  112. $this->userSession
  113. );
  114. }
  115. public function dataNoCSRFRequiredPublicPage(): array {
  116. return [
  117. ['testAnnotationNoCSRFRequiredPublicPage'],
  118. ['testAnnotationNoCSRFRequiredAttributePublicPage'],
  119. ['testAnnotationPublicPageAttributeNoCSRFRequired'],
  120. ['testAttributeNoCSRFRequiredPublicPage'],
  121. ];
  122. }
  123. public function dataPublicPage(): array {
  124. return [
  125. ['testAnnotationPublicPage'],
  126. ['testAttributePublicPage'],
  127. ];
  128. }
  129. public function dataNoCSRFRequired(): array {
  130. return [
  131. ['testAnnotationNoCSRFRequired'],
  132. ['testAttributeNoCSRFRequired'],
  133. ];
  134. }
  135. public function dataPublicPageStrictCookieRequired(): array {
  136. return [
  137. ['testAnnotationPublicPageStrictCookieRequired'],
  138. ['testAnnotationStrictCookieRequiredAttributePublicPage'],
  139. ['testAnnotationPublicPageAttributeStrictCookiesRequired'],
  140. ['testAttributePublicPageStrictCookiesRequired'],
  141. ];
  142. }
  143. public function dataNoCSRFRequiredPublicPageStrictCookieRequired(): array {
  144. return [
  145. ['testAnnotationNoCSRFRequiredPublicPageStrictCookieRequired'],
  146. ['testAttributeNoCSRFRequiredPublicPageStrictCookiesRequired'],
  147. ];
  148. }
  149. public function dataNoAdminRequiredNoCSRFRequired(): array {
  150. return [
  151. ['testAnnotationNoAdminRequiredNoCSRFRequired'],
  152. ['testAttributeNoAdminRequiredNoCSRFRequired'],
  153. ];
  154. }
  155. public function dataNoAdminRequiredNoCSRFRequiredPublicPage(): array {
  156. return [
  157. ['testAnnotationNoAdminRequiredNoCSRFRequiredPublicPage'],
  158. ['testAttributeNoAdminRequiredNoCSRFRequiredPublicPage'],
  159. ];
  160. }
  161. public function dataNoCSRFRequiredSubAdminRequired(): array {
  162. return [
  163. ['testAnnotationNoCSRFRequiredSubAdminRequired'],
  164. ['testAnnotationNoCSRFRequiredAttributeSubAdminRequired'],
  165. ['testAnnotationSubAdminRequiredAttributeNoCSRFRequired'],
  166. ['testAttributeNoCSRFRequiredSubAdminRequired'],
  167. ];
  168. }
  169. /**
  170. * @dataProvider dataNoCSRFRequiredPublicPage
  171. */
  172. public function testSetNavigationEntry(string $method): void {
  173. $this->navigationManager->expects($this->once())
  174. ->method('setActiveEntry')
  175. ->with($this->equalTo('files'));
  176. $this->reader->reflect($this->controller, $method);
  177. $this->middleware->beforeController($this->controller, $method);
  178. }
  179. /**
  180. * @param string $method
  181. * @param string $test
  182. */
  183. private function ajaxExceptionStatus($method, $test, $status) {
  184. $isLoggedIn = false;
  185. $isAdminUser = false;
  186. // isAdminUser requires isLoggedIn call to return true
  187. if ($test === 'isAdminUser') {
  188. $isLoggedIn = true;
  189. }
  190. $sec = $this->getMiddleware($isLoggedIn, $isAdminUser, false);
  191. try {
  192. $this->reader->reflect($this->controller, $method);
  193. $sec->beforeController($this->controller, $method);
  194. } catch (SecurityException $ex) {
  195. $this->assertEquals($status, $ex->getCode());
  196. }
  197. // add assertion if everything should work fine otherwise phpunit will
  198. // complain
  199. if ($status === 0) {
  200. $this->addToAssertionCount(1);
  201. }
  202. }
  203. public function testAjaxStatusLoggedInCheck(): void {
  204. $this->ajaxExceptionStatus(
  205. 'testNoAnnotationNorAttribute',
  206. 'isLoggedIn',
  207. Http::STATUS_UNAUTHORIZED
  208. );
  209. }
  210. /**
  211. * @dataProvider dataNoCSRFRequired
  212. */
  213. public function testAjaxNotAdminCheck(string $method): void {
  214. $this->ajaxExceptionStatus(
  215. $method,
  216. 'isAdminUser',
  217. Http::STATUS_FORBIDDEN
  218. );
  219. }
  220. /**
  221. * @dataProvider dataPublicPage
  222. */
  223. public function testAjaxStatusCSRFCheck(string $method): void {
  224. $this->ajaxExceptionStatus(
  225. $method,
  226. 'passesCSRFCheck',
  227. Http::STATUS_PRECONDITION_FAILED
  228. );
  229. }
  230. /**
  231. * @dataProvider dataNoCSRFRequiredPublicPage
  232. */
  233. public function testAjaxStatusAllGood(string $method): void {
  234. $this->ajaxExceptionStatus(
  235. $method,
  236. 'isLoggedIn',
  237. 0
  238. );
  239. $this->ajaxExceptionStatus(
  240. $method,
  241. 'isAdminUser',
  242. 0
  243. );
  244. $this->ajaxExceptionStatus(
  245. $method,
  246. 'passesCSRFCheck',
  247. 0
  248. );
  249. }
  250. /**
  251. * @dataProvider dataNoCSRFRequiredPublicPage
  252. */
  253. public function testNoChecks(string $method): void {
  254. $this->request->expects($this->never())
  255. ->method('passesCSRFCheck')
  256. ->willReturn(false);
  257. $sec = $this->getMiddleware(false, false, false);
  258. $this->reader->reflect($this->controller, $method);
  259. $sec->beforeController($this->controller, $method);
  260. }
  261. /**
  262. * @param string $method
  263. * @param string $expects
  264. */
  265. private function securityCheck($method, $expects, $shouldFail = false) {
  266. // admin check requires login
  267. if ($expects === 'isAdminUser') {
  268. $isLoggedIn = true;
  269. $isAdminUser = !$shouldFail;
  270. } else {
  271. $isLoggedIn = !$shouldFail;
  272. $isAdminUser = false;
  273. }
  274. $sec = $this->getMiddleware($isLoggedIn, $isAdminUser, false);
  275. if ($shouldFail) {
  276. $this->expectException(SecurityException::class);
  277. } else {
  278. $this->addToAssertionCount(1);
  279. }
  280. $this->reader->reflect($this->controller, $method);
  281. $sec->beforeController($this->controller, $method);
  282. }
  283. /**
  284. * @dataProvider dataPublicPage
  285. */
  286. public function testCsrfCheck(string $method): void {
  287. $this->expectException(\OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException::class);
  288. $this->request->expects($this->once())
  289. ->method('passesCSRFCheck')
  290. ->willReturn(false);
  291. $this->request->expects($this->once())
  292. ->method('passesStrictCookieCheck')
  293. ->willReturn(true);
  294. $this->reader->reflect($this->controller, $method);
  295. $this->middleware->beforeController($this->controller, $method);
  296. }
  297. /**
  298. * @dataProvider dataNoCSRFRequiredPublicPage
  299. */
  300. public function testNoCsrfCheck(string $method) {
  301. $this->request->expects($this->never())
  302. ->method('passesCSRFCheck')
  303. ->willReturn(false);
  304. $this->reader->reflect($this->controller, $method);
  305. $this->middleware->beforeController($this->controller, $method);
  306. }
  307. /**
  308. * @dataProvider dataPublicPage
  309. */
  310. public function testPassesCsrfCheck(string $method): void {
  311. $this->request->expects($this->once())
  312. ->method('passesCSRFCheck')
  313. ->willReturn(true);
  314. $this->request->expects($this->once())
  315. ->method('passesStrictCookieCheck')
  316. ->willReturn(true);
  317. $this->reader->reflect($this->controller, $method);
  318. $this->middleware->beforeController($this->controller, $method);
  319. }
  320. /**
  321. * @dataProvider dataPublicPage
  322. */
  323. public function testFailCsrfCheck(string $method): void {
  324. $this->expectException(\OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException::class);
  325. $this->request->expects($this->once())
  326. ->method('passesCSRFCheck')
  327. ->willReturn(false);
  328. $this->request->expects($this->once())
  329. ->method('passesStrictCookieCheck')
  330. ->willReturn(true);
  331. $this->reader->reflect($this->controller, $method);
  332. $this->middleware->beforeController($this->controller, $method);
  333. }
  334. /**
  335. * @dataProvider dataPublicPageStrictCookieRequired
  336. */
  337. public function testStrictCookieRequiredCheck(string $method): void {
  338. $this->expectException(\OC\AppFramework\Middleware\Security\Exceptions\StrictCookieMissingException::class);
  339. $this->request->expects($this->never())
  340. ->method('passesCSRFCheck');
  341. $this->request->expects($this->once())
  342. ->method('passesStrictCookieCheck')
  343. ->willReturn(false);
  344. $this->reader->reflect($this->controller, $method);
  345. $this->middleware->beforeController($this->controller, $method);
  346. }
  347. /**
  348. * @dataProvider dataNoCSRFRequiredPublicPage
  349. */
  350. public function testNoStrictCookieRequiredCheck(string $method): void {
  351. $this->request->expects($this->never())
  352. ->method('passesStrictCookieCheck')
  353. ->willReturn(false);
  354. $this->reader->reflect($this->controller, $method);
  355. $this->middleware->beforeController($this->controller, $method);
  356. }
  357. /**
  358. * @dataProvider dataNoCSRFRequiredPublicPageStrictCookieRequired
  359. */
  360. public function testPassesStrictCookieRequiredCheck(string $method): void {
  361. $this->request
  362. ->expects($this->once())
  363. ->method('passesStrictCookieCheck')
  364. ->willReturn(true);
  365. $this->reader->reflect($this->controller, $method);
  366. $this->middleware->beforeController($this->controller, $method);
  367. }
  368. public function dataCsrfOcsController(): array {
  369. return [
  370. [NormalController::class, false, false, true],
  371. [NormalController::class, false, true, true],
  372. [NormalController::class, true, false, true],
  373. [NormalController::class, true, true, true],
  374. [OCSController::class, false, false, true],
  375. [OCSController::class, false, true, false],
  376. [OCSController::class, true, false, false],
  377. [OCSController::class, true, true, false],
  378. ];
  379. }
  380. /**
  381. * @dataProvider dataCsrfOcsController
  382. * @param string $controllerClass
  383. * @param bool $hasOcsApiHeader
  384. * @param bool $hasBearerAuth
  385. * @param bool $exception
  386. */
  387. public function testCsrfOcsController(string $controllerClass, bool $hasOcsApiHeader, bool $hasBearerAuth, bool $exception): void {
  388. $this->request
  389. ->method('getHeader')
  390. ->willReturnCallback(function ($header) use ($hasOcsApiHeader, $hasBearerAuth) {
  391. if ($header === 'OCS-APIREQUEST' && $hasOcsApiHeader) {
  392. return 'true';
  393. }
  394. if ($header === 'Authorization' && $hasBearerAuth) {
  395. return 'Bearer TOKEN!';
  396. }
  397. return '';
  398. });
  399. $this->request->expects($this->once())
  400. ->method('passesStrictCookieCheck')
  401. ->willReturn(true);
  402. $controller = new $controllerClass('test', $this->request);
  403. try {
  404. $this->middleware->beforeController($controller, 'foo');
  405. $this->assertFalse($exception);
  406. } catch (CrossSiteRequestForgeryException $e) {
  407. $this->assertTrue($exception);
  408. }
  409. }
  410. /**
  411. * @dataProvider dataNoAdminRequiredNoCSRFRequired
  412. */
  413. public function testLoggedInCheck(string $method): void {
  414. $this->securityCheck($method, 'isLoggedIn');
  415. }
  416. /**
  417. * @dataProvider dataNoAdminRequiredNoCSRFRequired
  418. */
  419. public function testFailLoggedInCheck(string $method): void {
  420. $this->securityCheck($method, 'isLoggedIn', true);
  421. }
  422. /**
  423. * @dataProvider dataNoCSRFRequired
  424. */
  425. public function testIsAdminCheck(string $method): void {
  426. $this->securityCheck($method, 'isAdminUser');
  427. }
  428. /**
  429. * @dataProvider dataNoCSRFRequiredSubAdminRequired
  430. */
  431. public function testIsNotSubAdminCheck(string $method): void {
  432. $this->reader->reflect($this->controller, $method);
  433. $sec = $this->getMiddleware(true, false, false);
  434. $this->expectException(SecurityException::class);
  435. $sec->beforeController($this->controller, $method);
  436. }
  437. /**
  438. * @dataProvider dataNoCSRFRequiredSubAdminRequired
  439. */
  440. public function testIsSubAdminCheck(string $method): void {
  441. $this->reader->reflect($this->controller, $method);
  442. $sec = $this->getMiddleware(true, false, true);
  443. $sec->beforeController($this->controller, $method);
  444. $this->addToAssertionCount(1);
  445. }
  446. /**
  447. * @dataProvider dataNoCSRFRequiredSubAdminRequired
  448. */
  449. public function testIsSubAdminAndAdminCheck(string $method): void {
  450. $this->reader->reflect($this->controller, $method);
  451. $sec = $this->getMiddleware(true, true, true);
  452. $sec->beforeController($this->controller, $method);
  453. $this->addToAssertionCount(1);
  454. }
  455. /**
  456. * @dataProvider dataNoCSRFRequired
  457. */
  458. public function testFailIsAdminCheck(string $method): void {
  459. $this->securityCheck($method, 'isAdminUser', true);
  460. }
  461. /**
  462. * @dataProvider dataNoAdminRequiredNoCSRFRequiredPublicPage
  463. */
  464. public function testRestrictedAppLoggedInPublicPage(string $method): void {
  465. $middleware = $this->getMiddleware(true, false, false);
  466. $this->reader->reflect($this->controller, $method);
  467. $this->appManager->method('getAppPath')
  468. ->with('files')
  469. ->willReturn('foo');
  470. $this->appManager->method('isEnabledForUser')
  471. ->with('files')
  472. ->willReturn(false);
  473. $middleware->beforeController($this->controller, $method);
  474. $this->addToAssertionCount(1);
  475. }
  476. /**
  477. * @dataProvider dataNoAdminRequiredNoCSRFRequiredPublicPage
  478. */
  479. public function testRestrictedAppNotLoggedInPublicPage(string $method): void {
  480. $middleware = $this->getMiddleware(false, false, false);
  481. $this->reader->reflect($this->controller, $method);
  482. $this->appManager->method('getAppPath')
  483. ->with('files')
  484. ->willReturn('foo');
  485. $this->appManager->method('isEnabledForUser')
  486. ->with('files')
  487. ->willReturn(false);
  488. $middleware->beforeController($this->controller, $method);
  489. $this->addToAssertionCount(1);
  490. }
  491. /**
  492. * @dataProvider dataNoAdminRequiredNoCSRFRequired
  493. */
  494. public function testRestrictedAppLoggedIn(string $method): void {
  495. $middleware = $this->getMiddleware(true, false, false, false);
  496. $this->reader->reflect($this->controller, $method);
  497. $this->appManager->method('getAppPath')
  498. ->with('files')
  499. ->willReturn('foo');
  500. $this->expectException(AppNotEnabledException::class);
  501. $middleware->beforeController($this->controller, $method);
  502. }
  503. public function testAfterExceptionNotCaughtThrowsItAgain() {
  504. $ex = new \Exception();
  505. $this->expectException(\Exception::class);
  506. $this->middleware->afterException($this->controller, 'test', $ex);
  507. }
  508. public function testAfterExceptionReturnsRedirectForNotLoggedInUser() {
  509. $this->request = new Request(
  510. [
  511. 'server' =>
  512. [
  513. 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  514. 'REQUEST_URI' => 'nextcloud/index.php/apps/specialapp'
  515. ]
  516. ],
  517. $this->createMock(IRequestId::class),
  518. $this->createMock(IConfig::class)
  519. );
  520. $this->middleware = $this->getMiddleware(false, false, false);
  521. $this->urlGenerator
  522. ->expects($this->once())
  523. ->method('linkToRoute')
  524. ->with(
  525. 'core.login.showLoginForm',
  526. [
  527. 'redirect_url' => 'nextcloud/index.php/apps/specialapp',
  528. ]
  529. )
  530. ->willReturn('http://localhost/nextcloud/index.php/login?redirect_url=nextcloud/index.php/apps/specialapp');
  531. $this->logger
  532. ->expects($this->once())
  533. ->method('debug');
  534. $response = $this->middleware->afterException(
  535. $this->controller,
  536. 'test',
  537. new NotLoggedInException()
  538. );
  539. $expected = new RedirectResponse('http://localhost/nextcloud/index.php/login?redirect_url=nextcloud/index.php/apps/specialapp');
  540. $this->assertEquals($expected, $response);
  541. }
  542. public function testAfterExceptionRedirectsToWebRootAfterStrictCookieFail() {
  543. $this->request = new Request(
  544. [
  545. 'server' => [
  546. 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  547. 'REQUEST_URI' => 'nextcloud/index.php/apps/specialapp',
  548. ],
  549. ],
  550. $this->createMock(IRequestId::class),
  551. $this->createMock(IConfig::class)
  552. );
  553. $this->middleware = $this->getMiddleware(false, false, false);
  554. $response = $this->middleware->afterException(
  555. $this->controller,
  556. 'test',
  557. new StrictCookieMissingException()
  558. );
  559. $expected = new RedirectResponse(\OC::$WEBROOT . '/');
  560. $this->assertEquals($expected, $response);
  561. }
  562. /**
  563. * @return array
  564. */
  565. public function exceptionProvider() {
  566. return [
  567. [
  568. new AppNotEnabledException(),
  569. ],
  570. [
  571. new CrossSiteRequestForgeryException(),
  572. ],
  573. [
  574. new NotAdminException(''),
  575. ],
  576. ];
  577. }
  578. /**
  579. * @dataProvider exceptionProvider
  580. * @param SecurityException $exception
  581. */
  582. public function testAfterExceptionReturnsTemplateResponse(SecurityException $exception) {
  583. $this->request = new Request(
  584. [
  585. 'server' =>
  586. [
  587. 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  588. 'REQUEST_URI' => 'nextcloud/index.php/apps/specialapp'
  589. ]
  590. ],
  591. $this->createMock(IRequestId::class),
  592. $this->createMock(IConfig::class)
  593. );
  594. $this->middleware = $this->getMiddleware(false, false, false);
  595. $this->logger
  596. ->expects($this->once())
  597. ->method('debug');
  598. $response = $this->middleware->afterException(
  599. $this->controller,
  600. 'test',
  601. $exception
  602. );
  603. $expected = new TemplateResponse('core', '403', ['message' => $exception->getMessage()], 'guest');
  604. $expected->setStatus($exception->getCode());
  605. $this->assertEquals($expected, $response);
  606. }
  607. public function testAfterAjaxExceptionReturnsJSONError() {
  608. $response = $this->middleware->afterException($this->controller, 'test',
  609. $this->secAjaxException);
  610. $this->assertTrue($response instanceof JSONResponse);
  611. }
  612. }