SecurityMiddlewareTest.php 22 KB

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