RedirectResponseSubscriberTest.php 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. <?php
  2. namespace Drupal\Tests\Core\EventSubscriber;
  3. use Drupal\Core\EventSubscriber\RedirectResponseSubscriber;
  4. use Drupal\Core\Routing\TrustedRedirectResponse;
  5. use Drupal\Core\Utility\UnroutedUrlAssemblerInterface;
  6. use Drupal\Tests\UnitTestCase;
  7. use Symfony\Component\DependencyInjection\Container;
  8. use Symfony\Component\EventDispatcher\EventDispatcher;
  9. use Symfony\Component\HttpFoundation\RedirectResponse;
  10. use Symfony\Component\HttpFoundation\Request;
  11. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  12. use Symfony\Component\HttpKernel\HttpKernelInterface;
  13. use Symfony\Component\HttpKernel\KernelEvents;
  14. /**
  15. * @coversDefaultClass \Drupal\Core\EventSubscriber\RedirectResponseSubscriber
  16. * @group EventSubscriber
  17. */
  18. class RedirectResponseSubscriberTest extends UnitTestCase {
  19. /**
  20. * The mocked request context.
  21. *
  22. * @var \Drupal\Core\Routing\RequestContext|\PHPUnit_Framework_MockObject_MockObject
  23. */
  24. protected $requestContext;
  25. /**
  26. * The mocked request context.
  27. *
  28. * @var \Drupal\Core\Utility\UnroutedUrlAssemblerInterface|\PHPUnit_Framework_MockObject_MockObject
  29. */
  30. protected $urlAssembler;
  31. /**
  32. * {@inheritdoc}
  33. */
  34. protected function setUp() {
  35. parent::setUp();
  36. $this->requestContext = $this->getMockBuilder('Drupal\Core\Routing\RequestContext')
  37. ->disableOriginalConstructor()
  38. ->getMock();
  39. $this->requestContext->expects($this->any())
  40. ->method('getCompleteBaseUrl')
  41. ->willReturn('http://example.com/drupal');
  42. $this->urlAssembler = $this->getMock(UnroutedUrlAssemblerInterface::class);
  43. $this->urlAssembler
  44. ->expects($this->any())
  45. ->method('assemble')
  46. ->willReturnMap([
  47. ['base:test', ['query' => [], 'fragment' => '', 'absolute' => TRUE], FALSE, 'http://example.com/drupal/test'],
  48. ['base:example.com', ['query' => [], 'fragment' => '', 'absolute' => TRUE], FALSE, 'http://example.com/drupal/example.com'],
  49. ['base:example:com', ['query' => [], 'fragment' => '', 'absolute' => TRUE], FALSE, 'http://example.com/drupal/example:com'],
  50. ['base:javascript:alert(0)', ['query' => [], 'fragment' => '', 'absolute' => TRUE], FALSE, 'http://example.com/drupal/javascript:alert(0)'],
  51. ]);
  52. $container = new Container();
  53. $container->set('router.request_context', $this->requestContext);
  54. \Drupal::setContainer($container);
  55. }
  56. /**
  57. * Test destination detection and redirection.
  58. *
  59. * @param \Symfony\Component\HttpFoundation\Request $request
  60. * The request object with destination query set.
  61. * @param string|bool $expected
  62. * The expected target URL or FALSE.
  63. *
  64. * @covers ::checkRedirectUrl
  65. * @dataProvider providerTestDestinationRedirect
  66. */
  67. public function testDestinationRedirect(Request $request, $expected) {
  68. $dispatcher = new EventDispatcher();
  69. $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
  70. $response = new RedirectResponse('http://example.com/drupal');
  71. $request->headers->set('HOST', 'example.com');
  72. $listener = new RedirectResponseSubscriber($this->urlAssembler, $this->requestContext);
  73. $dispatcher->addListener(KernelEvents::RESPONSE, [$listener, 'checkRedirectUrl']);
  74. $event = new FilterResponseEvent($kernel, $request, HttpKernelInterface::SUB_REQUEST, $response);
  75. $dispatcher->dispatch(KernelEvents::RESPONSE, $event);
  76. $target_url = $event->getResponse()->getTargetUrl();
  77. if ($expected) {
  78. $this->assertEquals($expected, $target_url);
  79. }
  80. else {
  81. $this->assertEquals('http://example.com/drupal', $target_url);
  82. }
  83. }
  84. /**
  85. * Data provider for testDestinationRedirect().
  86. *
  87. * @see \Drupal\Tests\Core\EventSubscriber\RedirectResponseSubscriberTest::testDestinationRedirect()
  88. */
  89. public static function providerTestDestinationRedirect() {
  90. return [
  91. [new Request(), FALSE],
  92. [new Request(['destination' => 'test']), 'http://example.com/drupal/test'],
  93. [new Request(['destination' => '/drupal/test']), 'http://example.com/drupal/test'],
  94. [new Request(['destination' => 'example.com']), 'http://example.com/drupal/example.com'],
  95. [new Request(['destination' => 'example:com']), 'http://example.com/drupal/example:com'],
  96. [new Request(['destination' => 'javascript:alert(0)']), 'http://example.com/drupal/javascript:alert(0)'],
  97. [new Request(['destination' => 'http://example.com/drupal/']), 'http://example.com/drupal/'],
  98. [new Request(['destination' => 'http://example.com/drupal/test']), 'http://example.com/drupal/test'],
  99. ];
  100. }
  101. /**
  102. * @dataProvider providerTestDestinationRedirectToExternalUrl
  103. */
  104. public function testDestinationRedirectToExternalUrl($request, $expected) {
  105. $dispatcher = new EventDispatcher();
  106. $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
  107. $response = new RedirectResponse('http://other-example.com');
  108. $listener = new RedirectResponseSubscriber($this->urlAssembler, $this->requestContext);
  109. $dispatcher->addListener(KernelEvents::RESPONSE, [$listener, 'checkRedirectUrl']);
  110. $event = new FilterResponseEvent($kernel, $request, HttpKernelInterface::SUB_REQUEST, $response);
  111. $this->setExpectedException(\PHPUnit_Framework_Error::class);
  112. $dispatcher->dispatch(KernelEvents::RESPONSE, $event);
  113. }
  114. /**
  115. * @covers ::checkRedirectUrl
  116. */
  117. public function testRedirectWithOptInExternalUrl() {
  118. $dispatcher = new EventDispatcher();
  119. $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
  120. $response = new TrustedRedirectResponse('http://external-url.com');
  121. $request = Request::create('');
  122. $request->headers->set('HOST', 'example.com');
  123. $listener = new RedirectResponseSubscriber($this->urlAssembler, $this->requestContext);
  124. $dispatcher->addListener(KernelEvents::RESPONSE, [$listener, 'checkRedirectUrl']);
  125. $event = new FilterResponseEvent($kernel, $request, HttpKernelInterface::SUB_REQUEST, $response);
  126. $dispatcher->dispatch(KernelEvents::RESPONSE, $event);
  127. $target_url = $event->getResponse()->getTargetUrl();
  128. $this->assertEquals('http://external-url.com', $target_url);
  129. }
  130. /**
  131. * Data provider for testDestinationRedirectToExternalUrl().
  132. */
  133. public function providerTestDestinationRedirectToExternalUrl() {
  134. return [
  135. 'absolute external url' => [new Request(['destination' => 'http://example.com']), 'http://example.com'],
  136. 'absolute external url with folder' => [new Request(['destination' => 'http://example.com/foobar']), 'http://example.com/foobar'],
  137. 'absolute external url with folder2' => [new Request(['destination' => 'http://example.ca/drupal']), 'http://example.ca/drupal'],
  138. 'path without drupal basepath' => [new Request(['destination' => '/test']), 'http://example.com/test'],
  139. 'path with URL' => [new Request(['destination' => '/example.com']), 'http://example.com/example.com'],
  140. 'path with URL and two slashes' => [new Request(['destination' => '//example.com']), 'http://example.com//example.com'],
  141. ];
  142. }
  143. /**
  144. * @dataProvider providerTestDestinationRedirectWithInvalidUrl
  145. */
  146. public function testDestinationRedirectWithInvalidUrl(Request $request) {
  147. $dispatcher = new EventDispatcher();
  148. $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
  149. $response = new RedirectResponse('http://example.com/drupal');
  150. $listener = new RedirectResponseSubscriber($this->urlAssembler, $this->requestContext);
  151. $dispatcher->addListener(KernelEvents::RESPONSE, [$listener, 'checkRedirectUrl']);
  152. $event = new FilterResponseEvent($kernel, $request, HttpKernelInterface::SUB_REQUEST, $response);
  153. $this->setExpectedException(\PHPUnit_Framework_Error::class);
  154. $dispatcher->dispatch(KernelEvents::RESPONSE, $event);
  155. }
  156. /**
  157. * Data provider for testDestinationRedirectWithInvalidUrl().
  158. */
  159. public function providerTestDestinationRedirectWithInvalidUrl() {
  160. $data = [];
  161. $data[] = [new Request(['destination' => '//example:com'])];
  162. $data[] = [new Request(['destination' => '//example:com/test'])];
  163. $data['absolute external url'] = [new Request(['destination' => 'http://example.com'])];
  164. $data['absolute external url with folder'] = [new Request(['destination' => 'http://example.ca/drupal'])];
  165. $data['path without drupal basepath'] = [new Request(['destination' => '/test'])];
  166. $data['path with URL'] = [new Request(['destination' => '/example.com'])];
  167. $data['path with URL and two slashes'] = [new Request(['destination' => '//example.com'])];
  168. return $data;
  169. }
  170. }