You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2116 lines
49 KiB

10 years ago
10 years ago
10 years ago
10 years ago
  1. <?php
  2. /**
  3. * @copyright 2013 Thomas Tanghus (thomas@tanghus.net)
  4. * @copyright 2016 Lukas Reschke lukas@owncloud.com
  5. *
  6. * This file is licensed under the Affero General Public License version 3 or
  7. * later.
  8. * See the COPYING-README file.
  9. */
  10. namespace Test\AppFramework\Http;
  11. use OC\AppFramework\Http\Request;
  12. use OC\Security\CSRF\CsrfToken;
  13. use OC\Security\CSRF\CsrfTokenManager;
  14. use OCP\IConfig;
  15. use OCP\Security\ISecureRandom;
  16. /**
  17. * Class RequestTest
  18. *
  19. * @package OC\AppFramework\Http
  20. */
  21. class RequestTest extends \Test\TestCase {
  22. /** @var string */
  23. protected $stream = 'fakeinput://data';
  24. /** @var ISecureRandom */
  25. protected $secureRandom;
  26. /** @var IConfig */
  27. protected $config;
  28. /** @var CsrfTokenManager */
  29. protected $csrfTokenManager;
  30. protected function setUp(): void {
  31. parent::setUp();
  32. if (in_array('fakeinput', stream_get_wrappers())) {
  33. stream_wrapper_unregister('fakeinput');
  34. }
  35. stream_wrapper_register('fakeinput', 'Test\AppFramework\Http\RequestStream');
  36. $this->secureRandom = $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock();
  37. $this->config = $this->getMockBuilder(IConfig::class)->getMock();
  38. $this->csrfTokenManager = $this->getMockBuilder('\OC\Security\CSRF\CsrfTokenManager')
  39. ->disableOriginalConstructor()->getMock();
  40. }
  41. protected function tearDown(): void {
  42. stream_wrapper_unregister('fakeinput');
  43. parent::tearDown();
  44. }
  45. public function testRequestAccessors() {
  46. $vars = [
  47. 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'],
  48. 'method' => 'GET',
  49. ];
  50. $request = new Request(
  51. $vars,
  52. $this->secureRandom,
  53. $this->config,
  54. $this->csrfTokenManager,
  55. $this->stream
  56. );
  57. // Countable
  58. $this->assertSame(2, count($request));
  59. // Array access
  60. $this->assertSame('Joey', $request['nickname']);
  61. // "Magic" accessors
  62. $this->assertSame('Joey', $request->{'nickname'});
  63. $this->assertTrue(isset($request['nickname']));
  64. $this->assertTrue(isset($request->{'nickname'}));
  65. $this->assertFalse(isset($request->{'flickname'}));
  66. // Only testing 'get', but same approach for post, files etc.
  67. $this->assertSame('Joey', $request->get['nickname']);
  68. // Always returns null if variable not set.
  69. $this->assertSame(null, $request->{'flickname'});
  70. }
  71. // urlParams has precedence over POST which has precedence over GET
  72. public function testPrecedence() {
  73. $vars = [
  74. 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'],
  75. 'post' => ['name' => 'Jane Doe', 'nickname' => 'Janey'],
  76. 'urlParams' => ['user' => 'jw', 'name' => 'Johnny Weissmüller'],
  77. 'method' => 'GET'
  78. ];
  79. $request = new Request(
  80. $vars,
  81. $this->secureRandom,
  82. $this->config,
  83. $this->csrfTokenManager,
  84. $this->stream
  85. );
  86. $this->assertSame(3, count($request));
  87. $this->assertSame('Janey', $request->{'nickname'});
  88. $this->assertSame('Johnny Weissmüller', $request->{'name'});
  89. }
  90. public function testImmutableArrayAccess() {
  91. $this->expectException(\RuntimeException::class);
  92. $vars = [
  93. 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'],
  94. 'method' => 'GET'
  95. ];
  96. $request = new Request(
  97. $vars,
  98. $this->secureRandom,
  99. $this->config,
  100. $this->csrfTokenManager,
  101. $this->stream
  102. );
  103. $request['nickname'] = 'Janey';
  104. }
  105. public function testImmutableMagicAccess() {
  106. $this->expectException(\RuntimeException::class);
  107. $vars = [
  108. 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'],
  109. 'method' => 'GET'
  110. ];
  111. $request = new Request(
  112. $vars,
  113. $this->secureRandom,
  114. $this->config,
  115. $this->csrfTokenManager,
  116. $this->stream
  117. );
  118. $request->{'nickname'} = 'Janey';
  119. }
  120. public function testGetTheMethodRight() {
  121. $this->expectException(\LogicException::class);
  122. $vars = [
  123. 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'],
  124. 'method' => 'GET',
  125. ];
  126. $request = new Request(
  127. $vars,
  128. $this->secureRandom,
  129. $this->config,
  130. $this->csrfTokenManager,
  131. $this->stream
  132. );
  133. $request->post;
  134. }
  135. public function testTheMethodIsRight() {
  136. $vars = [
  137. 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'],
  138. 'method' => 'GET',
  139. ];
  140. $request = new Request(
  141. $vars,
  142. $this->secureRandom,
  143. $this->config,
  144. $this->csrfTokenManager,
  145. $this->stream
  146. );
  147. $this->assertSame('GET', $request->method);
  148. $result = $request->get;
  149. $this->assertSame('John Q. Public', $result['name']);
  150. $this->assertSame('Joey', $result['nickname']);
  151. }
  152. public function testJsonPost() {
  153. global $data;
  154. $data = '{"name": "John Q. Public", "nickname": "Joey"}';
  155. $vars = [
  156. 'method' => 'POST',
  157. 'server' => ['CONTENT_TYPE' => 'application/json; utf-8']
  158. ];
  159. $request = new Request(
  160. $vars,
  161. $this->secureRandom,
  162. $this->config,
  163. $this->csrfTokenManager,
  164. $this->stream
  165. );
  166. $this->assertSame('POST', $request->method);
  167. $result = $request->post;
  168. $this->assertSame('John Q. Public', $result['name']);
  169. $this->assertSame('Joey', $result['nickname']);
  170. $this->assertSame('Joey', $request->params['nickname']);
  171. $this->assertSame('Joey', $request['nickname']);
  172. }
  173. public function testNotJsonPost() {
  174. global $data;
  175. $data = 'this is not valid json';
  176. $vars = [
  177. 'method' => 'POST',
  178. 'server' => ['CONTENT_TYPE' => 'application/json; utf-8']
  179. ];
  180. $request = new Request(
  181. $vars,
  182. $this->secureRandom,
  183. $this->config,
  184. $this->csrfTokenManager,
  185. $this->stream
  186. );
  187. $this->assertEquals('POST', $request->method);
  188. $result = $request->post;
  189. // ensure there's no error attempting to decode the content
  190. }
  191. public function testPatch() {
  192. global $data;
  193. $data = http_build_query(['name' => 'John Q. Public', 'nickname' => 'Joey'], '', '&');
  194. $vars = [
  195. 'method' => 'PATCH',
  196. 'server' => ['CONTENT_TYPE' => 'application/x-www-form-urlencoded'],
  197. ];
  198. $request = new Request(
  199. $vars,
  200. $this->secureRandom,
  201. $this->config,
  202. $this->csrfTokenManager,
  203. $this->stream
  204. );
  205. $this->assertSame('PATCH', $request->method);
  206. $result = $request->patch;
  207. $this->assertSame('John Q. Public', $result['name']);
  208. $this->assertSame('Joey', $result['nickname']);
  209. }
  210. public function testJsonPatchAndPut() {
  211. global $data;
  212. // PUT content
  213. $data = '{"name": "John Q. Public", "nickname": "Joey"}';
  214. $vars = [
  215. 'method' => 'PUT',
  216. 'server' => ['CONTENT_TYPE' => 'application/json; utf-8'],
  217. ];
  218. $request = new Request(
  219. $vars,
  220. $this->secureRandom,
  221. $this->config,
  222. $this->csrfTokenManager,
  223. $this->stream
  224. );
  225. $this->assertSame('PUT', $request->method);
  226. $result = $request->put;
  227. $this->assertSame('John Q. Public', $result['name']);
  228. $this->assertSame('Joey', $result['nickname']);
  229. // PATCH content
  230. $data = '{"name": "John Q. Public", "nickname": null}';
  231. $vars = [
  232. 'method' => 'PATCH',
  233. 'server' => ['CONTENT_TYPE' => 'application/json; utf-8'],
  234. ];
  235. $request = new Request(
  236. $vars,
  237. $this->secureRandom,
  238. $this->config,
  239. $this->csrfTokenManager,
  240. $this->stream
  241. );
  242. $this->assertSame('PATCH', $request->method);
  243. $result = $request->patch;
  244. $this->assertSame('John Q. Public', $result['name']);
  245. $this->assertSame(null, $result['nickname']);
  246. }
  247. public function testPutStream() {
  248. global $data;
  249. $data = file_get_contents(__DIR__ . '/../../../data/testimage.png');
  250. $vars = [
  251. 'put' => $data,
  252. 'method' => 'PUT',
  253. 'server' => [
  254. 'CONTENT_TYPE' => 'image/png',
  255. 'CONTENT_LENGTH' => (string)strlen($data)
  256. ],
  257. ];
  258. $request = new Request(
  259. $vars,
  260. $this->secureRandom,
  261. $this->config,
  262. $this->csrfTokenManager,
  263. $this->stream
  264. );
  265. $this->assertSame('PUT', $request->method);
  266. $resource = $request->put;
  267. $contents = stream_get_contents($resource);
  268. $this->assertSame($data, $contents);
  269. try {
  270. $resource = $request->put;
  271. } catch (\LogicException $e) {
  272. return;
  273. }
  274. $this->fail('Expected LogicException.');
  275. }
  276. public function testSetUrlParameters() {
  277. $vars = [
  278. 'post' => [],
  279. 'method' => 'POST',
  280. 'urlParams' => ['id' => '2'],
  281. ];
  282. $request = new Request(
  283. $vars,
  284. $this->secureRandom,
  285. $this->config,
  286. $this->csrfTokenManager,
  287. $this->stream
  288. );
  289. $newParams = ['id' => '3', 'test' => 'test2'];
  290. $request->setUrlParameters($newParams);
  291. $this->assertSame('test2', $request->getParam('test'));
  292. $this->assertEquals('3', $request->getParam('id'));
  293. $this->assertEquals('3', $request->getParams()['id']);
  294. }
  295. public function testGetIdWithModUnique() {
  296. $vars = [
  297. 'server' => [
  298. 'UNIQUE_ID' => 'GeneratedUniqueIdByModUnique'
  299. ],
  300. ];
  301. $request = new Request(
  302. $vars,
  303. $this->secureRandom,
  304. $this->config,
  305. $this->csrfTokenManager,
  306. $this->stream
  307. );
  308. $this->assertSame('GeneratedUniqueIdByModUnique', $request->getId());
  309. }
  310. public function testGetIdWithoutModUnique() {
  311. $this->secureRandom->expects($this->once())
  312. ->method('generate')
  313. ->with('20')
  314. ->willReturn('GeneratedByOwnCloudItself');
  315. $request = new Request(
  316. [],
  317. $this->secureRandom,
  318. $this->config,
  319. $this->csrfTokenManager,
  320. $this->stream
  321. );
  322. $this->assertSame('GeneratedByOwnCloudItself', $request->getId());
  323. }
  324. public function testGetIdWithoutModUniqueStable() {
  325. $request = new Request(
  326. [],
  327. \OC::$server->getSecureRandom(),
  328. $this->config,
  329. $this->csrfTokenManager,
  330. $this->stream
  331. );
  332. $firstId = $request->getId();
  333. $secondId = $request->getId();
  334. $this->assertSame($firstId, $secondId);
  335. }
  336. public function testGetRemoteAddressWithoutTrustedRemote() {
  337. $this->config
  338. ->expects($this->once())
  339. ->method('getSystemValue')
  340. ->with('trusted_proxies')
  341. ->willReturn([]);
  342. $request = new Request(
  343. [
  344. 'server' => [
  345. 'REMOTE_ADDR' => '10.0.0.2',
  346. 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
  347. 'HTTP_X_FORWARDED_FOR' => '192.168.0.233'
  348. ],
  349. ],
  350. $this->secureRandom,
  351. $this->config,
  352. $this->csrfTokenManager,
  353. $this->stream
  354. );
  355. $this->assertSame('10.0.0.2', $request->getRemoteAddress());
  356. }
  357. public function testGetRemoteAddressWithNoTrustedHeader() {
  358. $this->config
  359. ->expects($this->at(0))
  360. ->method('getSystemValue')
  361. ->with('trusted_proxies')
  362. ->willReturn(['10.0.0.2']);
  363. $this->config
  364. ->expects($this->at(1))
  365. ->method('getSystemValue')
  366. ->with('forwarded_for_headers')
  367. ->willReturn([]);
  368. $request = new Request(
  369. [
  370. 'server' => [
  371. 'REMOTE_ADDR' => '10.0.0.2',
  372. 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
  373. 'HTTP_X_FORWARDED_FOR' => '192.168.0.233'
  374. ],
  375. ],
  376. $this->secureRandom,
  377. $this->config,
  378. $this->csrfTokenManager,
  379. $this->stream
  380. );
  381. $this->assertSame('10.0.0.2', $request->getRemoteAddress());
  382. }
  383. public function testGetRemoteAddressWithSingleTrustedRemote() {
  384. $this->config
  385. ->expects($this->at(0))
  386. ->method('getSystemValue')
  387. ->with('trusted_proxies')
  388. ->willReturn(['10.0.0.2']);
  389. $this->config
  390. ->expects($this->at(1))
  391. ->method('getSystemValue')
  392. ->with('forwarded_for_headers')
  393. ->willReturn(['HTTP_X_FORWARDED']);
  394. $request = new Request(
  395. [
  396. 'server' => [
  397. 'REMOTE_ADDR' => '10.0.0.2',
  398. 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
  399. 'HTTP_X_FORWARDED_FOR' => '192.168.0.233'
  400. ],
  401. ],
  402. $this->secureRandom,
  403. $this->config,
  404. $this->csrfTokenManager,
  405. $this->stream
  406. );
  407. $this->assertSame('10.4.0.5', $request->getRemoteAddress());
  408. }
  409. public function testGetRemoteAddressIPv6WithSingleTrustedRemote() {
  410. $this->config
  411. ->expects($this->at(0))
  412. ->method('getSystemValue')
  413. ->with('trusted_proxies')
  414. ->willReturn(['2001:db8:85a3:8d3:1319:8a2e:370:7348']);
  415. $this->config
  416. ->expects($this->at(1))
  417. ->method('getSystemValue')
  418. ->with('forwarded_for_headers')
  419. ->willReturn(['HTTP_X_FORWARDED']);
  420. $request = new Request(
  421. [
  422. 'server' => [
  423. 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348',
  424. 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
  425. 'HTTP_X_FORWARDED_FOR' => '192.168.0.233'
  426. ],
  427. ],
  428. $this->secureRandom,
  429. $this->config,
  430. $this->csrfTokenManager,
  431. $this->stream
  432. );
  433. $this->assertSame('10.4.0.5', $request->getRemoteAddress());
  434. }
  435. public function testGetRemoteAddressVerifyPriorityHeader() {
  436. $this->config
  437. ->expects($this->at(0))
  438. ->method('getSystemValue')
  439. ->with('trusted_proxies')
  440. ->willReturn(['10.0.0.2']);
  441. $this->config
  442. ->expects($this->at(1))
  443. ->method('getSystemValue')
  444. ->with('forwarded_for_headers')
  445. ->willReturn([
  446. 'HTTP_CLIENT_IP',
  447. 'HTTP_X_FORWARDED_FOR',
  448. 'HTTP_X_FORWARDED'
  449. ]);
  450. $request = new Request(
  451. [
  452. 'server' => [
  453. 'REMOTE_ADDR' => '10.0.0.2',
  454. 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
  455. 'HTTP_X_FORWARDED_FOR' => '192.168.0.233'
  456. ],
  457. ],
  458. $this->secureRandom,
  459. $this->config,
  460. $this->csrfTokenManager,
  461. $this->stream
  462. );
  463. $this->assertSame('192.168.0.233', $request->getRemoteAddress());
  464. }
  465. public function testGetRemoteAddressIPv6VerifyPriorityHeader() {
  466. $this->config
  467. ->expects($this->at(0))
  468. ->method('getSystemValue')
  469. ->with('trusted_proxies')
  470. ->willReturn(['2001:db8:85a3:8d3:1319:8a2e:370:7348']);
  471. $this->config
  472. ->expects($this->at(1))
  473. ->method('getSystemValue')
  474. ->with('forwarded_for_headers')
  475. ->willReturn([
  476. 'HTTP_CLIENT_IP',
  477. 'HTTP_X_FORWARDED_FOR',
  478. 'HTTP_X_FORWARDED'
  479. ]);
  480. $request = new Request(
  481. [
  482. 'server' => [
  483. 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348',
  484. 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
  485. 'HTTP_X_FORWARDED_FOR' => '192.168.0.233'
  486. ],
  487. ],
  488. $this->secureRandom,
  489. $this->config,
  490. $this->csrfTokenManager,
  491. $this->stream
  492. );
  493. $this->assertSame('192.168.0.233', $request->getRemoteAddress());
  494. }
  495. public function testGetRemoteAddressWithMatchingCidrTrustedRemote() {
  496. $this->config
  497. ->expects($this->at(0))
  498. ->method('getSystemValue')
  499. ->with('trusted_proxies')
  500. ->willReturn(['192.168.2.0/24']);
  501. $this->config
  502. ->expects($this->at(1))
  503. ->method('getSystemValue')
  504. ->with('forwarded_for_headers')
  505. ->willReturn(['HTTP_X_FORWARDED_FOR']);
  506. $request = new Request(
  507. [
  508. 'server' => [
  509. 'REMOTE_ADDR' => '192.168.2.99',
  510. 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
  511. 'HTTP_X_FORWARDED_FOR' => '192.168.0.233'
  512. ],
  513. ],
  514. $this->secureRandom,
  515. $this->config,
  516. $this->csrfTokenManager,
  517. $this->stream
  518. );
  519. $this->assertSame('192.168.0.233', $request->getRemoteAddress());
  520. }
  521. public function testGetRemoteAddressWithNotMatchingCidrTrustedRemote() {
  522. $this->config
  523. ->expects($this->once())
  524. ->method('getSystemValue')
  525. ->with('trusted_proxies')
  526. ->willReturn(['192.168.2.0/24']);
  527. $request = new Request(
  528. [
  529. 'server' => [
  530. 'REMOTE_ADDR' => '192.168.3.99',
  531. 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
  532. 'HTTP_X_FORWARDED_FOR' => '192.168.0.233'
  533. ],
  534. ],
  535. $this->secureRandom,
  536. $this->config,
  537. $this->csrfTokenManager,
  538. $this->stream
  539. );
  540. $this->assertSame('192.168.3.99', $request->getRemoteAddress());
  541. }
  542. /**
  543. * @return array
  544. */
  545. public function httpProtocolProvider() {
  546. return [
  547. // Valid HTTP 1.0
  548. ['HTTP/1.0', 'HTTP/1.0'],
  549. ['http/1.0', 'HTTP/1.0'],
  550. ['HTTp/1.0', 'HTTP/1.0'],
  551. // Valid HTTP 1.1
  552. ['HTTP/1.1', 'HTTP/1.1'],
  553. ['http/1.1', 'HTTP/1.1'],
  554. ['HTTp/1.1', 'HTTP/1.1'],
  555. // Valid HTTP 2.0
  556. ['HTTP/2', 'HTTP/2'],
  557. ['http/2', 'HTTP/2'],
  558. ['HTTp/2', 'HTTP/2'],
  559. // Invalid
  560. ['HTTp/394', 'HTTP/1.1'],
  561. ['InvalidProvider/1.1', 'HTTP/1.1'],
  562. [null, 'HTTP/1.1'],
  563. ['', 'HTTP/1.1'],
  564. ];
  565. }
  566. /**
  567. * @dataProvider httpProtocolProvider
  568. *
  569. * @param mixed $input
  570. * @param string $expected
  571. */
  572. public function testGetHttpProtocol($input, $expected) {
  573. $request = new Request(
  574. [
  575. 'server' => [
  576. 'SERVER_PROTOCOL' => $input,
  577. ],
  578. ],
  579. $this->secureRandom,
  580. $this->config,
  581. $this->csrfTokenManager,
  582. $this->stream
  583. );
  584. $this->assertSame($expected, $request->getHttpProtocol());
  585. }
  586. public function testGetServerProtocolWithOverride() {
  587. $this->config
  588. ->expects($this->at(0))
  589. ->method('getSystemValue')
  590. ->with('overwriteprotocol')
  591. ->willReturn('customProtocol');
  592. $this->config
  593. ->expects($this->at(1))
  594. ->method('getSystemValue')
  595. ->with('overwritecondaddr')
  596. ->willReturn('');
  597. $this->config
  598. ->expects($this->at(2))
  599. ->method('getSystemValue')
  600. ->with('overwriteprotocol')
  601. ->willReturn('customProtocol');
  602. $request = new Request(
  603. [],
  604. $this->secureRandom,
  605. $this->config,
  606. $this->csrfTokenManager,
  607. $this->stream
  608. );
  609. $this->assertSame('customProtocol', $request->getServerProtocol());
  610. }
  611. public function testGetServerProtocolWithProtoValid() {
  612. $this->config
  613. ->method('getSystemValue')
  614. ->willReturnCallback(function ($key, $default) {
  615. if ($key === 'trusted_proxies') {
  616. return ['1.2.3.4'];
  617. }
  618. return $default;
  619. });
  620. $requestHttps = new Request(
  621. [
  622. 'server' => [
  623. 'HTTP_X_FORWARDED_PROTO' => 'HtTpS',
  624. 'REMOTE_ADDR' => '1.2.3.4',
  625. ],
  626. ],
  627. $this->secureRandom,
  628. $this->config,
  629. $this->csrfTokenManager,
  630. $this->stream
  631. );
  632. $requestHttp = new Request(
  633. [
  634. 'server' => [
  635. 'HTTP_X_FORWARDED_PROTO' => 'HTTp',
  636. 'REMOTE_ADDR' => '1.2.3.4',
  637. ],
  638. ],
  639. $this->secureRandom,
  640. $this->config,
  641. $this->csrfTokenManager,
  642. $this->stream
  643. );
  644. $this->assertSame('https', $requestHttps->getServerProtocol());
  645. $this->assertSame('http', $requestHttp->getServerProtocol());
  646. }
  647. public function testGetServerProtocolWithHttpsServerValueOn() {
  648. $this->config
  649. ->method('getSystemValue')
  650. ->willReturnCallback(function ($key, $default) {
  651. return $default;
  652. });
  653. $request = new Request(
  654. [
  655. 'server' => [
  656. 'HTTPS' => 'on'
  657. ],
  658. ],
  659. $this->secureRandom,
  660. $this->config,
  661. $this->csrfTokenManager,
  662. $this->stream
  663. );
  664. $this->assertSame('https', $request->getServerProtocol());
  665. }
  666. public function testGetServerProtocolWithHttpsServerValueOff() {
  667. $this->config
  668. ->method('getSystemValue')
  669. ->willReturnCallback(function ($key, $default) {
  670. return $default;
  671. });
  672. $request = new Request(
  673. [
  674. 'server' => [
  675. 'HTTPS' => 'off'
  676. ],
  677. ],
  678. $this->secureRandom,
  679. $this->config,
  680. $this->csrfTokenManager,
  681. $this->stream
  682. );
  683. $this->assertSame('http', $request->getServerProtocol());
  684. }
  685. public function testGetServerProtocolWithHttpsServerValueEmpty() {
  686. $this->config
  687. ->method('getSystemValue')
  688. ->willReturnCallback(function ($key, $default) {
  689. return $default;
  690. });
  691. $request = new Request(
  692. [
  693. 'server' => [
  694. 'HTTPS' => ''
  695. ],
  696. ],
  697. $this->secureRandom,
  698. $this->config,
  699. $this->csrfTokenManager,
  700. $this->stream
  701. );
  702. $this->assertSame('http', $request->getServerProtocol());
  703. }
  704. public function testGetServerProtocolDefault() {
  705. $this->config
  706. ->method('getSystemValue')
  707. ->willReturnCallback(function ($key, $default) {
  708. return $default;
  709. });
  710. $request = new Request(
  711. [],
  712. $this->secureRandom,
  713. $this->config,
  714. $this->csrfTokenManager,
  715. $this->stream
  716. );
  717. $this->assertSame('http', $request->getServerProtocol());
  718. }
  719. public function testGetServerProtocolBehindLoadBalancers() {
  720. $this->config
  721. ->method('getSystemValue')
  722. ->willReturnCallback(function ($key, $default) {
  723. if ($key === 'trusted_proxies') {
  724. return ['1.2.3.4'];
  725. }
  726. return $default;
  727. });
  728. $request = new Request(
  729. [
  730. 'server' => [
  731. 'HTTP_X_FORWARDED_PROTO' => 'https,http,http',
  732. 'REMOTE_ADDR' => '1.2.3.4',
  733. ],
  734. ],
  735. $this->secureRandom,
  736. $this->config,
  737. $this->csrfTokenManager,
  738. $this->stream
  739. );
  740. $this->assertSame('https', $request->getServerProtocol());
  741. }
  742. /**
  743. * @dataProvider userAgentProvider
  744. * @param string $testAgent
  745. * @param array $userAgent
  746. * @param bool $matches
  747. */
  748. public function testUserAgent($testAgent, $userAgent, $matches) {
  749. $request = new Request(
  750. [
  751. 'server' => [
  752. 'HTTP_USER_AGENT' => $testAgent,
  753. ]
  754. ],
  755. $this->secureRandom,
  756. $this->config,
  757. $this->csrfTokenManager,
  758. $this->stream
  759. );
  760. $this->assertSame($matches, $request->isUserAgent($userAgent));
  761. }
  762. /**
  763. * @dataProvider userAgentProvider
  764. * @param string $testAgent
  765. * @param array $userAgent
  766. * @param bool $matches
  767. */
  768. public function testUndefinedUserAgent($testAgent, $userAgent, $matches) {
  769. $request = new Request(
  770. [],
  771. $this->secureRandom,
  772. $this->config,
  773. $this->csrfTokenManager,
  774. $this->stream
  775. );
  776. $this->assertFalse($request->isUserAgent($userAgent));
  777. }
  778. /**
  779. * @return array
  780. */
  781. public function userAgentProvider() {
  782. return [
  783. [
  784. 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
  785. [
  786. Request::USER_AGENT_IE
  787. ],
  788. true,
  789. ],
  790. [
  791. 'Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20100101 Firefox/24.0',
  792. [
  793. Request::USER_AGENT_IE
  794. ],
  795. false,
  796. ],
  797. [
  798. 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36',
  799. [
  800. Request::USER_AGENT_CHROME
  801. ],
  802. true,
  803. ],
  804. [
  805. 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/53.0.2785.143 Chrome/53.0.2785.143 Safari/537.36',
  806. [
  807. Request::USER_AGENT_CHROME
  808. ],
  809. true,
  810. ],
  811. [
  812. 'Mozilla/5.0 (Linux; Android 4.4; Nexus 4 Build/KRT16S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Mobile Safari/537.36',
  813. [
  814. Request::USER_AGENT_ANDROID_MOBILE_CHROME
  815. ],
  816. true,
  817. ],
  818. [
  819. 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
  820. [
  821. Request::USER_AGENT_ANDROID_MOBILE_CHROME
  822. ],
  823. false,
  824. ],
  825. [
  826. 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
  827. [
  828. Request::USER_AGENT_IE,
  829. Request::USER_AGENT_ANDROID_MOBILE_CHROME,
  830. ],
  831. true,
  832. ],
  833. [
  834. 'Mozilla/5.0 (Linux; Android 4.4; Nexus 4 Build/KRT16S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Mobile Safari/537.36',
  835. [
  836. Request::USER_AGENT_IE,
  837. Request::USER_AGENT_ANDROID_MOBILE_CHROME,
  838. ],
  839. true,
  840. ],
  841. [
  842. 'Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20100101 Firefox/24.0',
  843. [
  844. Request::USER_AGENT_FREEBOX
  845. ],
  846. false,
  847. ],
  848. [
  849. 'Mozilla/5.0',
  850. [
  851. Request::USER_AGENT_FREEBOX
  852. ],
  853. true,
  854. ],
  855. [
  856. 'Fake Mozilla/5.0',
  857. [
  858. Request::USER_AGENT_FREEBOX
  859. ],
  860. false,
  861. ],
  862. [
  863. 'Mozilla/5.0 (Android) ownCloud-android/2.0.0',
  864. [
  865. Request::USER_AGENT_CLIENT_ANDROID
  866. ],
  867. true,
  868. ],
  869. [
  870. 'Mozilla/5.0 (Android) Nextcloud-android/2.0.0',
  871. [
  872. Request::USER_AGENT_CLIENT_ANDROID
  873. ],
  874. true,
  875. ],
  876. [
  877. 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.99 Safari/537.36 Vivaldi/2.9.1705.41',
  878. [
  879. Request::USER_AGENT_CHROME
  880. ],
  881. true
  882. ],
  883. [
  884. 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.38 Safari/537.36 Brave/75',
  885. [
  886. Request::USER_AGENT_CHROME
  887. ],
  888. true
  889. ],
  890. [
  891. 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 OPR/50.0.2762.67',
  892. [
  893. Request::USER_AGENT_CHROME
  894. ],
  895. true
  896. ]
  897. ];
  898. }
  899. public function testInsecureServerHostServerNameHeader() {
  900. $request = new Request(
  901. [
  902. 'server' => [
  903. 'SERVER_NAME' => 'from.server.name:8080',
  904. ]
  905. ],
  906. $this->secureRandom,
  907. $this->config,
  908. $this->csrfTokenManager,
  909. $this->stream
  910. );
  911. $this->assertSame('from.server.name:8080', $request->getInsecureServerHost());
  912. }
  913. public function testInsecureServerHostHttpHostHeader() {
  914. $request = new Request(
  915. [
  916. 'server' => [
  917. 'SERVER_NAME' => 'from.server.name:8080',
  918. 'HTTP_HOST' => 'from.host.header:8080',
  919. ]
  920. ],
  921. $this->secureRandom,
  922. $this->config,
  923. $this->csrfTokenManager,
  924. $this->stream
  925. );
  926. $this->assertSame('from.host.header:8080', $request->getInsecureServerHost());
  927. }
  928. public function testInsecureServerHostHttpFromForwardedHeaderSingle() {
  929. $this->config
  930. ->method('getSystemValue')
  931. ->willReturnCallback(function ($key, $default) {
  932. if ($key === 'trusted_proxies') {
  933. return ['1.2.3.4'];
  934. }
  935. return $default;
  936. });
  937. $request = new Request(
  938. [
  939. 'server' => [
  940. 'SERVER_NAME' => 'from.server.name:8080',
  941. 'HTTP_HOST' => 'from.host.header:8080',
  942. 'HTTP_X_FORWARDED_HOST' => 'from.forwarded.host:8080',
  943. 'REMOTE_ADDR' => '1.2.3.4',
  944. ]
  945. ],
  946. $this->secureRandom,
  947. $this->config,
  948. $this->csrfTokenManager,
  949. $this->stream
  950. );
  951. $this->assertSame('from.forwarded.host:8080', $request->getInsecureServerHost());
  952. }
  953. public function testInsecureServerHostHttpFromForwardedHeaderStacked() {
  954. $this->config
  955. ->method('getSystemValue')
  956. ->willReturnCallback(function ($key, $default) {
  957. if ($key === 'trusted_proxies') {
  958. return ['1.2.3.4'];
  959. }
  960. return $default;
  961. });
  962. $request = new Request(
  963. [
  964. 'server' => [
  965. 'SERVER_NAME' => 'from.server.name:8080',
  966. 'HTTP_HOST' => 'from.host.header:8080',
  967. 'HTTP_X_FORWARDED_HOST' => 'from.forwarded.host2:8080,another.one:9000',
  968. 'REMOTE_ADDR' => '1.2.3.4',
  969. ]
  970. ],
  971. $this->secureRandom,
  972. $this->config,
  973. $this->csrfTokenManager,
  974. $this->stream
  975. );
  976. $this->assertSame('from.forwarded.host2:8080', $request->getInsecureServerHost());
  977. }
  978. public function testGetServerHostWithOverwriteHost() {
  979. $this->config
  980. ->method('getSystemValue')
  981. ->willReturnCallback(function ($key, $default) {
  982. if ($key === 'overwritecondaddr') {
  983. return '';
  984. } elseif ($key === 'overwritehost') {
  985. return 'my.overwritten.host';
  986. }
  987. return $default;
  988. });
  989. $request = new Request(
  990. [],
  991. $this->secureRandom,
  992. $this->config,
  993. $this->csrfTokenManager,
  994. $this->stream
  995. );
  996. $this->assertSame('my.overwritten.host', $request->getServerHost());
  997. }
  998. public function testGetServerHostWithTrustedDomain() {
  999. $this->config
  1000. ->method('getSystemValue')
  1001. ->willReturnCallback(function ($key, $default) {
  1002. if ($key === 'trusted_proxies') {
  1003. return ['1.2.3.4'];
  1004. } elseif ($key === 'trusted_domains') {
  1005. return ['my.trusted.host'];
  1006. }
  1007. return $default;
  1008. });
  1009. $request = new Request(
  1010. [
  1011. 'server' => [
  1012. 'HTTP_X_FORWARDED_HOST' => 'my.trusted.host',
  1013. 'REMOTE_ADDR' => '1.2.3.4',
  1014. ],
  1015. ],
  1016. $this->secureRandom,
  1017. $this->config,
  1018. $this->csrfTokenManager,
  1019. $this->stream
  1020. );
  1021. $this->assertSame('my.trusted.host', $request->getServerHost());
  1022. }
  1023. public function testGetServerHostWithUntrustedDomain() {
  1024. $this->config
  1025. ->method('getSystemValue')
  1026. ->willReturnCallback(function ($key, $default) {
  1027. if ($key === 'trusted_proxies') {
  1028. return ['1.2.3.4'];
  1029. } elseif ($key === 'trusted_domains') {
  1030. return ['my.trusted.host'];
  1031. }
  1032. return $default;
  1033. });
  1034. $request = new Request(
  1035. [
  1036. 'server' => [
  1037. 'HTTP_X_FORWARDED_HOST' => 'my.untrusted.host',
  1038. 'REMOTE_ADDR' => '1.2.3.4',
  1039. ],
  1040. ],
  1041. $this->secureRandom,
  1042. $this->config,
  1043. $this->csrfTokenManager,
  1044. $this->stream
  1045. );
  1046. $this->assertSame('my.trusted.host', $request->getServerHost());
  1047. }
  1048. public function testGetServerHostWithNoTrustedDomain() {
  1049. $this->config
  1050. ->method('getSystemValue')
  1051. ->willReturnCallback(function ($key, $default) {
  1052. if ($key === 'trusted_proxies') {
  1053. return ['1.2.3.4'];
  1054. }
  1055. return $default;
  1056. });
  1057. $request = new Request(
  1058. [
  1059. 'server' => [
  1060. 'HTTP_X_FORWARDED_HOST' => 'my.untrusted.host',
  1061. 'REMOTE_ADDR' => '1.2.3.4',
  1062. ],
  1063. ],
  1064. $this->secureRandom,
  1065. $this->config,
  1066. $this->csrfTokenManager,
  1067. $this->stream
  1068. );
  1069. $this->assertSame('', $request->getServerHost());
  1070. }
  1071. /**
  1072. * @return array
  1073. */
  1074. public function dataGetServerHostTrustedDomain() {
  1075. return [
  1076. 'is array' => ['my.trusted.host', ['my.trusted.host']],
  1077. 'is array but undefined index 0' => ['my.trusted.host', [2 => 'my.trusted.host']],
  1078. 'is string' => ['my.trusted.host', 'my.trusted.host'],
  1079. 'is null' => ['', null],
  1080. ];
  1081. }
  1082. /**
  1083. * @dataProvider dataGetServerHostTrustedDomain
  1084. * @param $expected
  1085. * @param $trustedDomain
  1086. */
  1087. public function testGetServerHostTrustedDomain($expected, $trustedDomain) {
  1088. $this->config
  1089. ->method('getSystemValue')
  1090. ->willReturnCallback(function ($key, $default) use ($trustedDomain) {
  1091. if ($key === 'trusted_proxies') {
  1092. return ['1.2.3.4'];
  1093. }
  1094. if ($key === 'trusted_domains') {
  1095. return $trustedDomain;
  1096. }
  1097. return $default;
  1098. });
  1099. $request = new Request(
  1100. [
  1101. 'server' => [
  1102. 'HTTP_X_FORWARDED_HOST' => 'my.untrusted.host',
  1103. 'REMOTE_ADDR' => '1.2.3.4',
  1104. ],
  1105. ],
  1106. $this->secureRandom,
  1107. $this->config,
  1108. $this->csrfTokenManager,
  1109. $this->stream
  1110. );
  1111. $this->assertSame($expected, $request->getServerHost());
  1112. }
  1113. public function testGetOverwriteHostDefaultNull() {
  1114. $this->config
  1115. ->expects($this->once())
  1116. ->method('getSystemValue')
  1117. ->with('overwritehost')
  1118. ->willReturn('');
  1119. $request = new Request(
  1120. [],
  1121. $this->secureRandom,
  1122. $this->config,
  1123. $this->csrfTokenManager,
  1124. $this->stream
  1125. );
  1126. $this->assertNull(self::invokePrivate($request, 'getOverwriteHost'));
  1127. }
  1128. public function testGetOverwriteHostWithOverwrite() {
  1129. $this->config
  1130. ->expects($this->at(0))
  1131. ->method('getSystemValue')
  1132. ->with('overwritehost')
  1133. ->willReturn('www.owncloud.org');
  1134. $this->config
  1135. ->expects($this->at(1))
  1136. ->method('getSystemValue')
  1137. ->with('overwritecondaddr')
  1138. ->willReturn('');
  1139. $this->config
  1140. ->expects($this->at(2))
  1141. ->method('getSystemValue')
  1142. ->with('overwritehost')
  1143. ->willReturn('www.owncloud.org');
  1144. $request = new Request(
  1145. [],
  1146. $this->secureRandom,
  1147. $this->config,
  1148. $this->csrfTokenManager,
  1149. $this->stream
  1150. );
  1151. $this->assertSame('www.owncloud.org', self::invokePrivate($request, 'getOverwriteHost'));
  1152. }
  1153. public function testGetPathInfoNotProcessible() {
  1154. $this->expectException(\Exception::class);
  1155. $this->expectExceptionMessage('The requested uri(/foo.php) cannot be processed by the script \'/var/www/index.php\')');
  1156. $request = new Request(
  1157. [
  1158. 'server' => [
  1159. 'REQUEST_URI' => '/foo.php',
  1160. 'SCRIPT_NAME' => '/var/www/index.php',
  1161. ]
  1162. ],
  1163. $this->secureRandom,
  1164. $this->config,
  1165. $this->csrfTokenManager,
  1166. $this->stream
  1167. );
  1168. $request->getPathInfo();
  1169. }
  1170. public function testGetRawPathInfoNotProcessible() {
  1171. $this->expectException(\Exception::class);
  1172. $this->expectExceptionMessage('The requested uri(/foo.php) cannot be processed by the script \'/var/www/index.php\')');
  1173. $request = new Request(
  1174. [
  1175. 'server' => [
  1176. 'REQUEST_URI' => '/foo.php',
  1177. 'SCRIPT_NAME' => '/var/www/index.php',
  1178. ]
  1179. ],
  1180. $this->secureRandom,
  1181. $this->config,
  1182. $this->csrfTokenManager,
  1183. $this->stream
  1184. );
  1185. $request->getRawPathInfo();
  1186. }
  1187. /**
  1188. * @dataProvider genericPathInfoProvider
  1189. * @param string $requestUri
  1190. * @param string $scriptName
  1191. * @param string $expected
  1192. */
  1193. public function testGetPathInfoWithoutSetEnvGeneric($requestUri, $scriptName, $expected) {
  1194. $request = new Request(
  1195. [
  1196. 'server' => [
  1197. 'REQUEST_URI' => $requestUri,
  1198. 'SCRIPT_NAME' => $scriptName,
  1199. ]
  1200. ],
  1201. $this->secureRandom,
  1202. $this->config,
  1203. $this->csrfTokenManager,
  1204. $this->stream
  1205. );
  1206. $this->assertSame($expected, $request->getPathInfo());
  1207. }
  1208. /**
  1209. * @dataProvider genericPathInfoProvider
  1210. * @param string $requestUri
  1211. * @param string $scriptName
  1212. * @param string $expected
  1213. */
  1214. public function testGetRawPathInfoWithoutSetEnvGeneric($requestUri, $scriptName, $expected) {
  1215. $request = new Request(
  1216. [
  1217. 'server' => [
  1218. 'REQUEST_URI' => $requestUri,
  1219. 'SCRIPT_NAME' => $scriptName,
  1220. ]
  1221. ],
  1222. $this->secureRandom,
  1223. $this->config,
  1224. $this->csrfTokenManager,
  1225. $this->stream
  1226. );
  1227. $this->assertSame($expected, $request->getRawPathInfo());
  1228. }
  1229. /**
  1230. * @dataProvider rawPathInfoProvider
  1231. * @param string $requestUri
  1232. * @param string $scriptName
  1233. * @param string $expected
  1234. */
  1235. public function testGetRawPathInfoWithoutSetEnv($requestUri, $scriptName, $expected) {
  1236. $request = new Request(
  1237. [
  1238. 'server' => [
  1239. 'REQUEST_URI' => $requestUri,
  1240. 'SCRIPT_NAME' => $scriptName,
  1241. ]
  1242. ],
  1243. $this->secureRandom,
  1244. $this->config,
  1245. $this->csrfTokenManager,
  1246. $this->stream
  1247. );
  1248. $this->assertSame($expected, $request->getRawPathInfo());
  1249. }
  1250. /**
  1251. * @dataProvider pathInfoProvider
  1252. * @param string $requestUri
  1253. * @param string $scriptName
  1254. * @param string $expected
  1255. */
  1256. public function testGetPathInfoWithoutSetEnv($requestUri, $scriptName, $expected) {
  1257. $request = new Request(
  1258. [
  1259. 'server' => [
  1260. 'REQUEST_URI' => $requestUri,
  1261. 'SCRIPT_NAME' => $scriptName,
  1262. ]
  1263. ],
  1264. $this->secureRandom,
  1265. $this->config,
  1266. $this->csrfTokenManager,
  1267. $this->stream
  1268. );
  1269. $this->assertSame($expected, $request->getPathInfo());
  1270. }
  1271. /**
  1272. * @return array
  1273. */
  1274. public function genericPathInfoProvider() {
  1275. return [
  1276. ['/core/index.php?XDEBUG_SESSION_START=14600', '/core/index.php', ''],
  1277. ['/index.php/apps/files/', 'index.php', '/apps/files/'],
  1278. ['/index.php/apps/files/../&amp;/&?someQueryParameter=QueryParam', 'index.php', '/apps/files/../&amp;/&'],
  1279. ['/remote.php/漢字編碼方法 / 汉字编码方法', 'remote.php', '/漢字編碼方法 / 汉字编码方法'],
  1280. ['///removeTrailin//gSlashes///', 'remote.php', '/removeTrailin/gSlashes/'],
  1281. ['/', '/', ''],
  1282. ['', '', ''],
  1283. ];
  1284. }
  1285. /**
  1286. * @return array
  1287. */
  1288. public function rawPathInfoProvider() {
  1289. return [
  1290. ['/foo%2Fbar/subfolder', '', 'foo%2Fbar/subfolder'],
  1291. ];
  1292. }
  1293. /**
  1294. * @return array
  1295. */
  1296. public function pathInfoProvider() {
  1297. return [
  1298. ['/foo%2Fbar/subfolder', '', 'foo/bar/subfolder'],
  1299. ];
  1300. }
  1301. public function testGetRequestUriWithoutOverwrite() {
  1302. $this->config
  1303. ->expects($this->once())
  1304. ->method('getSystemValue')
  1305. ->with('overwritewebroot')
  1306. ->willReturn('');
  1307. $request = new Request(
  1308. [
  1309. 'server' => [
  1310. 'REQUEST_URI' => '/test.php'
  1311. ]
  1312. ],
  1313. $this->secureRandom,
  1314. $this->config,
  1315. $this->csrfTokenManager,
  1316. $this->stream
  1317. );
  1318. $this->assertSame('/test.php', $request->getRequestUri());
  1319. }
  1320. public function providesGetRequestUriWithOverwriteData() {
  1321. return [
  1322. ['/scriptname.php/some/PathInfo', '/owncloud/', ''],
  1323. ['/scriptname.php/some/PathInfo', '/owncloud/', '123'],
  1324. ];
  1325. }
  1326. /**
  1327. * @dataProvider providesGetRequestUriWithOverwriteData
  1328. */
  1329. public function testGetRequestUriWithOverwrite($expectedUri, $overwriteWebRoot, $overwriteCondAddr) {
  1330. $this->config
  1331. ->expects($this->at(0))
  1332. ->method('getSystemValue')
  1333. ->with('overwritewebroot')
  1334. ->willReturn($overwriteWebRoot);
  1335. $this->config
  1336. ->expects($this->at(1))
  1337. ->method('getSystemValue')
  1338. ->with('overwritecondaddr')
  1339. ->willReturn($overwriteCondAddr);
  1340. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1341. ->setMethods(['getScriptName'])
  1342. ->setConstructorArgs([
  1343. [
  1344. 'server' => [
  1345. 'REQUEST_URI' => '/test.php/some/PathInfo',
  1346. 'SCRIPT_NAME' => '/test.php',
  1347. ]
  1348. ],
  1349. $this->secureRandom,
  1350. $this->config,
  1351. $this->csrfTokenManager,
  1352. $this->stream
  1353. ])
  1354. ->getMock();
  1355. $request
  1356. ->expects($this->once())
  1357. ->method('getScriptName')
  1358. ->willReturn('/scriptname.php');
  1359. $this->assertSame($expectedUri, $request->getRequestUri());
  1360. }
  1361. public function testPassesCSRFCheckWithGet() {
  1362. /** @var Request $request */
  1363. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1364. ->setMethods(['getScriptName'])
  1365. ->setConstructorArgs([
  1366. [
  1367. 'get' => [
  1368. 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1369. ],
  1370. 'cookies' => [
  1371. 'nc_sameSiteCookiestrict' => 'true',
  1372. 'nc_sameSiteCookielax' => 'true',
  1373. ],
  1374. ],
  1375. $this->secureRandom,
  1376. $this->config,
  1377. $this->csrfTokenManager,
  1378. $this->stream
  1379. ])
  1380. ->getMock();
  1381. $token = new CsrfToken('AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds');
  1382. $this->csrfTokenManager
  1383. ->expects($this->once())
  1384. ->method('isTokenValid')
  1385. ->with($token)
  1386. ->willReturn(true);
  1387. $this->assertTrue($request->passesCSRFCheck());
  1388. }
  1389. public function testPassesCSRFCheckWithPost() {
  1390. /** @var Request $request */
  1391. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1392. ->setMethods(['getScriptName'])
  1393. ->setConstructorArgs([
  1394. [
  1395. 'post' => [
  1396. 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1397. ],
  1398. 'cookies' => [
  1399. 'nc_sameSiteCookiestrict' => 'true',
  1400. 'nc_sameSiteCookielax' => 'true',
  1401. ],
  1402. ],
  1403. $this->secureRandom,
  1404. $this->config,
  1405. $this->csrfTokenManager,
  1406. $this->stream
  1407. ])
  1408. ->getMock();
  1409. $token = new CsrfToken('AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds');
  1410. $this->csrfTokenManager
  1411. ->expects($this->once())
  1412. ->method('isTokenValid')
  1413. ->with($token)
  1414. ->willReturn(true);
  1415. $this->assertTrue($request->passesCSRFCheck());
  1416. }
  1417. public function testPassesCSRFCheckWithHeader() {
  1418. /** @var Request $request */
  1419. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1420. ->setMethods(['getScriptName'])
  1421. ->setConstructorArgs([
  1422. [
  1423. 'server' => [
  1424. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1425. ],
  1426. 'cookies' => [
  1427. 'nc_sameSiteCookiestrict' => 'true',
  1428. 'nc_sameSiteCookielax' => 'true',
  1429. ],
  1430. ],
  1431. $this->secureRandom,
  1432. $this->config,
  1433. $this->csrfTokenManager,
  1434. $this->stream
  1435. ])
  1436. ->getMock();
  1437. $token = new CsrfToken('AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds');
  1438. $this->csrfTokenManager
  1439. ->expects($this->once())
  1440. ->method('isTokenValid')
  1441. ->with($token)
  1442. ->willReturn(true);
  1443. $this->assertTrue($request->passesCSRFCheck());
  1444. }
  1445. public function testPassesCSRFCheckWithGetAndWithoutCookies() {
  1446. /** @var Request $request */
  1447. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1448. ->setMethods(['getScriptName'])
  1449. ->setConstructorArgs([
  1450. [
  1451. 'get' => [
  1452. 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1453. ],
  1454. ],
  1455. $this->secureRandom,
  1456. $this->config,
  1457. $this->csrfTokenManager,
  1458. $this->stream
  1459. ])
  1460. ->getMock();
  1461. $this->csrfTokenManager
  1462. ->expects($this->once())
  1463. ->method('isTokenValid')
  1464. ->willReturn(true);
  1465. $this->assertTrue($request->passesCSRFCheck());
  1466. }
  1467. public function testPassesCSRFCheckWithPostAndWithoutCookies() {
  1468. /** @var Request $request */
  1469. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1470. ->setMethods(['getScriptName'])
  1471. ->setConstructorArgs([
  1472. [
  1473. 'post' => [
  1474. 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1475. ],
  1476. ],
  1477. $this->secureRandom,
  1478. $this->config,
  1479. $this->csrfTokenManager,
  1480. $this->stream
  1481. ])
  1482. ->getMock();
  1483. $this->csrfTokenManager
  1484. ->expects($this->once())
  1485. ->method('isTokenValid')
  1486. ->willReturn(true);
  1487. $this->assertTrue($request->passesCSRFCheck());
  1488. }
  1489. public function testPassesCSRFCheckWithHeaderAndWithoutCookies() {
  1490. /** @var Request $request */
  1491. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1492. ->setMethods(['getScriptName'])
  1493. ->setConstructorArgs([
  1494. [
  1495. 'server' => [
  1496. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1497. ],
  1498. ],
  1499. $this->secureRandom,
  1500. $this->config,
  1501. $this->csrfTokenManager,
  1502. $this->stream
  1503. ])
  1504. ->getMock();
  1505. $this->csrfTokenManager
  1506. ->expects($this->once())
  1507. ->method('isTokenValid')
  1508. ->willReturn(true);
  1509. $this->assertTrue($request->passesCSRFCheck());
  1510. }
  1511. public function testFailsCSRFCheckWithHeaderAndNotAllChecksPassing() {
  1512. /** @var Request $request */
  1513. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1514. ->setMethods(['getScriptName'])
  1515. ->setConstructorArgs([
  1516. [
  1517. 'server' => [
  1518. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1519. ],
  1520. 'cookies' => [
  1521. session_name() => 'asdf',
  1522. 'nc_sameSiteCookiestrict' => 'true',
  1523. ],
  1524. ],
  1525. $this->secureRandom,
  1526. $this->config,
  1527. $this->csrfTokenManager,
  1528. $this->stream
  1529. ])
  1530. ->getMock();
  1531. $this->csrfTokenManager
  1532. ->expects($this->never())
  1533. ->method('isTokenValid');
  1534. $this->assertFalse($request->passesCSRFCheck());
  1535. }
  1536. public function testPassesStrictCookieCheckWithAllCookiesAndStrict() {
  1537. /** @var Request $request */
  1538. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1539. ->setMethods(['getScriptName', 'getCookieParams'])
  1540. ->setConstructorArgs([
  1541. [
  1542. 'server' => [
  1543. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1544. ],
  1545. 'cookies' => [
  1546. session_name() => 'asdf',
  1547. '__Host-nc_sameSiteCookiestrict' => 'true',
  1548. '__Host-nc_sameSiteCookielax' => 'true',
  1549. ],
  1550. ],
  1551. $this->secureRandom,
  1552. $this->config,
  1553. $this->csrfTokenManager,
  1554. $this->stream
  1555. ])
  1556. ->getMock();
  1557. $request
  1558. ->expects($this->any())
  1559. ->method('getCookieParams')
  1560. ->willReturn([
  1561. 'secure' => true,
  1562. 'path' => '/',
  1563. ]);
  1564. $this->assertTrue($request->passesStrictCookieCheck());
  1565. }
  1566. public function testFailsStrictCookieCheckWithAllCookiesAndMissingStrict() {
  1567. /** @var Request $request */
  1568. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1569. ->setMethods(['getScriptName', 'getCookieParams'])
  1570. ->setConstructorArgs([
  1571. [
  1572. 'server' => [
  1573. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1574. ],
  1575. 'cookies' => [
  1576. session_name() => 'asdf',
  1577. 'nc_sameSiteCookiestrict' => 'true',
  1578. 'nc_sameSiteCookielax' => 'true',
  1579. ],
  1580. ],
  1581. $this->secureRandom,
  1582. $this->config,
  1583. $this->csrfTokenManager,
  1584. $this->stream
  1585. ])
  1586. ->getMock();
  1587. $request
  1588. ->expects($this->any())
  1589. ->method('getCookieParams')
  1590. ->willReturn([
  1591. 'secure' => true,
  1592. 'path' => '/',
  1593. ]);
  1594. $this->assertFalse($request->passesStrictCookieCheck());
  1595. }
  1596. public function testGetCookieParams() {
  1597. /** @var Request $request */
  1598. $request = $this->getMockBuilder(Request::class)
  1599. ->setMethods(['getScriptName'])
  1600. ->setConstructorArgs([
  1601. [],
  1602. $this->secureRandom,
  1603. $this->config,
  1604. $this->csrfTokenManager,
  1605. $this->stream
  1606. ])
  1607. ->getMock();
  1608. $actual = $request->getCookieParams();
  1609. $this->assertSame(session_get_cookie_params(), $actual);
  1610. }
  1611. public function testPassesStrictCookieCheckWithAllCookies() {
  1612. /** @var Request $request */
  1613. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1614. ->setMethods(['getScriptName'])
  1615. ->setConstructorArgs([
  1616. [
  1617. 'server' => [
  1618. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1619. ],
  1620. 'cookies' => [
  1621. session_name() => 'asdf',
  1622. 'nc_sameSiteCookiestrict' => 'true',
  1623. 'nc_sameSiteCookielax' => 'true',
  1624. ],
  1625. ],
  1626. $this->secureRandom,
  1627. $this->config,
  1628. $this->csrfTokenManager,
  1629. $this->stream
  1630. ])
  1631. ->getMock();
  1632. $this->assertTrue($request->passesStrictCookieCheck());
  1633. }
  1634. public function testPassesStrictCookieCheckWithRandomCookies() {
  1635. /** @var Request $request */
  1636. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1637. ->setMethods(['getScriptName'])
  1638. ->setConstructorArgs([
  1639. [
  1640. 'server' => [
  1641. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1642. ],
  1643. 'cookies' => [
  1644. 'RandomCookie' => 'asdf',
  1645. ],
  1646. ],
  1647. $this->secureRandom,
  1648. $this->config,
  1649. $this->csrfTokenManager,
  1650. $this->stream
  1651. ])
  1652. ->getMock();
  1653. $this->assertTrue($request->passesStrictCookieCheck());
  1654. }
  1655. public function testFailsStrictCookieCheckWithSessionCookie() {
  1656. /** @var Request $request */
  1657. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1658. ->setMethods(['getScriptName'])
  1659. ->setConstructorArgs([
  1660. [
  1661. 'server' => [
  1662. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1663. ],
  1664. 'cookies' => [
  1665. session_name() => 'asdf',
  1666. ],
  1667. ],
  1668. $this->secureRandom,
  1669. $this->config,
  1670. $this->csrfTokenManager,
  1671. $this->stream
  1672. ])
  1673. ->getMock();
  1674. $this->assertFalse($request->passesStrictCookieCheck());
  1675. }
  1676. public function testFailsStrictCookieCheckWithRememberMeCookie() {
  1677. /** @var Request $request */
  1678. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1679. ->setMethods(['getScriptName'])
  1680. ->setConstructorArgs([
  1681. [
  1682. 'server' => [
  1683. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1684. ],
  1685. 'cookies' => [
  1686. 'nc_token' => 'asdf',
  1687. ],
  1688. ],
  1689. $this->secureRandom,
  1690. $this->config,
  1691. $this->csrfTokenManager,
  1692. $this->stream
  1693. ])
  1694. ->getMock();
  1695. $this->assertFalse($request->passesStrictCookieCheck());
  1696. }
  1697. public function testFailsCSRFCheckWithPostAndWithCookies() {
  1698. /** @var Request $request */
  1699. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1700. ->setMethods(['getScriptName'])
  1701. ->setConstructorArgs([
  1702. [
  1703. 'post' => [
  1704. 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1705. ],
  1706. 'cookies' => [
  1707. session_name() => 'asdf',
  1708. 'foo' => 'bar',
  1709. ],
  1710. ],
  1711. $this->secureRandom,
  1712. $this->config,
  1713. $this->csrfTokenManager,
  1714. $this->stream
  1715. ])
  1716. ->getMock();
  1717. $this->csrfTokenManager
  1718. ->expects($this->never())
  1719. ->method('isTokenValid');
  1720. $this->assertFalse($request->passesCSRFCheck());
  1721. }
  1722. public function testFailStrictCookieCheckWithOnlyLaxCookie() {
  1723. /** @var Request $request */
  1724. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1725. ->setMethods(['getScriptName'])
  1726. ->setConstructorArgs([
  1727. [
  1728. 'server' => [
  1729. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1730. ],
  1731. 'cookies' => [
  1732. session_name() => 'asdf',
  1733. 'nc_sameSiteCookielax' => 'true',
  1734. ],
  1735. ],
  1736. $this->secureRandom,
  1737. $this->config,
  1738. $this->csrfTokenManager,
  1739. $this->stream
  1740. ])
  1741. ->getMock();
  1742. $this->assertFalse($request->passesStrictCookieCheck());
  1743. }
  1744. public function testFailStrictCookieCheckWithOnlyStrictCookie() {
  1745. /** @var Request $request */
  1746. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1747. ->setMethods(['getScriptName'])
  1748. ->setConstructorArgs([
  1749. [
  1750. 'server' => [
  1751. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1752. ],
  1753. 'cookies' => [
  1754. session_name() => 'asdf',
  1755. 'nc_sameSiteCookiestrict' => 'true',
  1756. ],
  1757. ],
  1758. $this->secureRandom,
  1759. $this->config,
  1760. $this->csrfTokenManager,
  1761. $this->stream
  1762. ])
  1763. ->getMock();
  1764. $this->assertFalse($request->passesStrictCookieCheck());
  1765. }
  1766. public function testPassesLaxCookieCheck() {
  1767. /** @var Request $request */
  1768. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1769. ->setMethods(['getScriptName'])
  1770. ->setConstructorArgs([
  1771. [
  1772. 'server' => [
  1773. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1774. ],
  1775. 'cookies' => [
  1776. session_name() => 'asdf',
  1777. 'nc_sameSiteCookielax' => 'true',
  1778. ],
  1779. ],
  1780. $this->secureRandom,
  1781. $this->config,
  1782. $this->csrfTokenManager,
  1783. $this->stream
  1784. ])
  1785. ->getMock();
  1786. $this->assertTrue($request->passesLaxCookieCheck());
  1787. }
  1788. public function testFailsLaxCookieCheckWithOnlyStrictCookie() {
  1789. /** @var Request $request */
  1790. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1791. ->setMethods(['getScriptName'])
  1792. ->setConstructorArgs([
  1793. [
  1794. 'server' => [
  1795. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1796. ],
  1797. 'cookies' => [
  1798. session_name() => 'asdf',
  1799. 'nc_sameSiteCookiestrict' => 'true',
  1800. ],
  1801. ],
  1802. $this->secureRandom,
  1803. $this->config,
  1804. $this->csrfTokenManager,
  1805. $this->stream
  1806. ])
  1807. ->getMock();
  1808. $this->assertFalse($request->passesLaxCookieCheck());
  1809. }
  1810. public function testSkipCookieCheckForOCSRequests() {
  1811. /** @var Request $request */
  1812. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1813. ->setMethods(['getScriptName'])
  1814. ->setConstructorArgs([
  1815. [
  1816. 'server' => [
  1817. 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
  1818. 'HTTP_OCS_APIREQUEST' => 'true',
  1819. ],
  1820. 'cookies' => [
  1821. session_name() => 'asdf',
  1822. 'nc_sameSiteCookiestrict' => 'false',
  1823. ],
  1824. ],
  1825. $this->secureRandom,
  1826. $this->config,
  1827. $this->csrfTokenManager,
  1828. $this->stream
  1829. ])
  1830. ->getMock();
  1831. $this->assertTrue($request->passesStrictCookieCheck());
  1832. }
  1833. /**
  1834. * @return array
  1835. */
  1836. public function invalidTokenDataProvider() {
  1837. return [
  1838. ['InvalidSentToken'],
  1839. ['InvalidSentToken:InvalidSecret'],
  1840. [''],
  1841. ];
  1842. }
  1843. /**
  1844. * @dataProvider invalidTokenDataProvider
  1845. * @param string $invalidToken
  1846. */
  1847. public function testPassesCSRFCheckWithInvalidToken($invalidToken) {
  1848. /** @var Request $request */
  1849. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1850. ->setMethods(['getScriptName'])
  1851. ->setConstructorArgs([
  1852. [
  1853. 'server' => [
  1854. 'HTTP_REQUESTTOKEN' => $invalidToken,
  1855. ],
  1856. ],
  1857. $this->secureRandom,
  1858. $this->config,
  1859. $this->csrfTokenManager,
  1860. $this->stream
  1861. ])
  1862. ->getMock();
  1863. $token = new CsrfToken($invalidToken);
  1864. $this->csrfTokenManager
  1865. ->expects($this->any())
  1866. ->method('isTokenValid')
  1867. ->with($token)
  1868. ->willReturn(false);
  1869. $this->assertFalse($request->passesCSRFCheck());
  1870. }
  1871. public function testPassesCSRFCheckWithoutTokenFail() {
  1872. /** @var Request $request */
  1873. $request = $this->getMockBuilder('\OC\AppFramework\Http\Request')
  1874. ->setMethods(['getScriptName'])
  1875. ->setConstructorArgs([
  1876. [],
  1877. $this->secureRandom,
  1878. $this->config,
  1879. $this->csrfTokenManager,
  1880. $this->stream
  1881. ])
  1882. ->getMock();
  1883. $this->assertFalse($request->passesCSRFCheck());
  1884. }
  1885. }