PathValidatorTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. <?php
  2. namespace Drupal\Tests\Core\Path;
  3. use Drupal\Core\ParamConverter\ParamNotConvertedException;
  4. use Drupal\Core\Path\PathValidator;
  5. use Drupal\Tests\UnitTestCase;
  6. use Symfony\Cmf\Component\Routing\RouteObjectInterface;
  7. use Symfony\Component\HttpFoundation\ParameterBag;
  8. use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  9. use Symfony\Component\Routing\Exception\MethodNotAllowedException;
  10. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  11. /**
  12. * @coversDefaultClass \Drupal\Core\Path\PathValidator
  13. * @group Routing
  14. */
  15. class PathValidatorTest extends UnitTestCase {
  16. /**
  17. * The mocked access aware router.
  18. *
  19. * @var \Drupal\Core\Routing\AccessAwareRouterInterface|\PHPUnit\Framework\MockObject\MockObject
  20. */
  21. protected $accessAwareRouter;
  22. /**
  23. * The mocked access unaware router.
  24. * @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface|\PHPUnit\Framework\MockObject\MockObject
  25. */
  26. protected $accessUnawareRouter;
  27. /**
  28. * The mocked account.
  29. *
  30. * @var \Drupal\Core\Session\AccountInterface|\PHPUnit\Framework\MockObject\MockObject
  31. */
  32. protected $account;
  33. /**
  34. * The path processor.
  35. *
  36. * @var \Drupal\Core\PathProcessor\InboundPathProcessorInterface|\PHPUnit\Framework\MockObject\MockObject
  37. */
  38. protected $pathProcessor;
  39. /**
  40. * The tested path validator.
  41. *
  42. * @var \Drupal\Core\Path\PathValidator
  43. */
  44. protected $pathValidator;
  45. /**
  46. * {@inheritdoc}
  47. */
  48. protected function setUp() {
  49. parent::setUp();
  50. $this->accessAwareRouter = $this->createMock('Drupal\Core\Routing\AccessAwareRouterInterface');
  51. $this->accessUnawareRouter = $this->createMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
  52. $this->account = $this->createMock('Drupal\Core\Session\AccountInterface');
  53. $this->pathProcessor = $this->createMock('Drupal\Core\PathProcessor\InboundPathProcessorInterface');
  54. $this->pathValidator = new PathValidator($this->accessAwareRouter, $this->accessUnawareRouter, $this->account, $this->pathProcessor);
  55. }
  56. /**
  57. * Tests the isValid() method for the frontpage.
  58. *
  59. * @covers ::isValid
  60. */
  61. public function testIsValidWithFrontpage() {
  62. $this->accessAwareRouter->expects($this->never())
  63. ->method('match');
  64. $this->assertTrue($this->pathValidator->isValid('<front>'));
  65. }
  66. /**
  67. * Tests the isValid() method for <none> (used for jumplinks).
  68. *
  69. * @covers ::isValid
  70. */
  71. public function testIsValidWithNone() {
  72. $this->accessAwareRouter->expects($this->never())
  73. ->method('match');
  74. $this->assertTrue($this->pathValidator->isValid('<none>'));
  75. }
  76. /**
  77. * Tests the isValid() method for an external URL.
  78. *
  79. * @covers ::isValid
  80. */
  81. public function testIsValidWithExternalUrl() {
  82. $this->accessAwareRouter->expects($this->never())
  83. ->method('match');
  84. $this->assertTrue($this->pathValidator->isValid('https://www.drupal.org'));
  85. }
  86. /**
  87. * Tests the isValid() method with an invalid external URL.
  88. *
  89. * @covers ::isValid
  90. */
  91. public function testIsValidWithInvalidExternalUrl() {
  92. $this->accessAwareRouter->expects($this->never())
  93. ->method('match');
  94. $this->assertFalse($this->pathValidator->isValid('http://'));
  95. }
  96. /**
  97. * Tests the isValid() method with a 'link to any page' permission.
  98. *
  99. * @covers ::isValid
  100. * @covers ::getPathAttributes
  101. */
  102. public function testIsValidWithLinkToAnyPageAccount() {
  103. $this->account->expects($this->once())
  104. ->method('hasPermission')
  105. ->with('link to any page')
  106. ->willReturn(TRUE);
  107. $this->accessAwareRouter->expects($this->never())
  108. ->method('match');
  109. $this->accessUnawareRouter->expects($this->once())
  110. ->method('match')
  111. ->with('/test-path')
  112. ->willReturn([RouteObjectInterface::ROUTE_NAME => 'test_route', '_raw_variables' => new ParameterBag(['key' => 'value'])]);
  113. $this->pathProcessor->expects($this->once())
  114. ->method('processInbound')
  115. ->willReturnArgument(0);
  116. $this->assertTrue($this->pathValidator->isValid('test-path'));
  117. }
  118. /**
  119. * Tests the isValid() method without the 'link to any page' permission.
  120. *
  121. * @covers ::isValid
  122. */
  123. public function testIsValidWithoutLinkToAnyPageAccount() {
  124. $this->account->expects($this->once())
  125. ->method('hasPermission')
  126. ->with('link to any page')
  127. ->willReturn(FALSE);
  128. $this->accessUnawareRouter->expects($this->never())
  129. ->method('match');
  130. $this->accessAwareRouter->expects($this->once())
  131. ->method('match')
  132. ->with('/test-path')
  133. ->willReturn([RouteObjectInterface::ROUTE_NAME => 'test_route', '_raw_variables' => new ParameterBag(['key' => 'value'])]);
  134. $this->pathProcessor->expects($this->once())
  135. ->method('processInbound')
  136. ->willReturnArgument(0);
  137. $this->assertTrue($this->pathValidator->isValid('test-path'));
  138. }
  139. /**
  140. * Tests the isValid() method with a path alias.
  141. *
  142. * @covers ::isValid
  143. */
  144. public function testIsValidWithPathAlias() {
  145. $this->account->expects($this->once())
  146. ->method('hasPermission')
  147. ->with('link to any page')
  148. ->willReturn(FALSE);
  149. $this->accessUnawareRouter->expects($this->never())
  150. ->method('match');
  151. $this->accessAwareRouter->expects($this->once())
  152. ->method('match')
  153. ->with('/test-path')
  154. ->willReturn([RouteObjectInterface::ROUTE_NAME => 'test_route', '_raw_variables' => new ParameterBag(['key' => 'value'])]);
  155. $this->pathProcessor->expects($this->once())
  156. ->method('processInbound')
  157. ->with('/path-alias', $this->anything())
  158. ->willReturn('/test-path');
  159. $this->assertTrue($this->pathValidator->isValid('path-alias'));
  160. }
  161. /**
  162. * Tests the isValid() method with a user without access to the path.
  163. *
  164. * @covers ::isValid
  165. * @covers ::getPathAttributes
  166. */
  167. public function testIsValidWithAccessDenied() {
  168. $this->account->expects($this->once())
  169. ->method('hasPermission')
  170. ->with('link to any page')
  171. ->willReturn(FALSE);
  172. $this->accessUnawareRouter->expects($this->never())
  173. ->method('match');
  174. $this->accessAwareRouter->expects($this->once())
  175. ->method('match')
  176. ->with('/test-path')
  177. ->willThrowException(new AccessDeniedHttpException());
  178. $this->pathProcessor->expects($this->once())
  179. ->method('processInbound')
  180. ->willReturnArgument(0);
  181. $this->assertFalse($this->pathValidator->isValid('test-path'));
  182. }
  183. /**
  184. * @covers ::isValid
  185. * @covers ::getPathAttributes
  186. */
  187. public function testIsValidWithResourceNotFound() {
  188. $this->account->expects($this->once())
  189. ->method('hasPermission')
  190. ->with('link to any page')
  191. ->willReturn(FALSE);
  192. $this->accessUnawareRouter->expects($this->never())
  193. ->method('match');
  194. $this->accessAwareRouter->expects($this->once())
  195. ->method('match')
  196. ->with('/test-path')
  197. ->willThrowException(new ResourceNotFoundException());
  198. $this->pathProcessor->expects($this->once())
  199. ->method('processInbound')
  200. ->willReturnArgument(0);
  201. $this->assertFalse($this->pathValidator->isValid('test-path'));
  202. }
  203. /**
  204. * @covers ::isValid
  205. * @covers ::getPathAttributes
  206. */
  207. public function testIsValidWithParamNotConverted() {
  208. $this->account->expects($this->once())
  209. ->method('hasPermission')
  210. ->with('link to any page')
  211. ->willReturn(FALSE);
  212. $this->accessUnawareRouter->expects($this->never())
  213. ->method('match');
  214. $this->accessAwareRouter->expects($this->once())
  215. ->method('match')
  216. ->with('/test-path')
  217. ->willThrowException(new ParamNotConvertedException());
  218. $this->pathProcessor->expects($this->once())
  219. ->method('processInbound')
  220. ->willReturnArgument(0);
  221. $this->assertFalse($this->pathValidator->isValid('test-path'));
  222. }
  223. /**
  224. * @covers ::isValid
  225. * @covers ::getPathAttributes
  226. */
  227. public function testIsValidWithMethodNotAllowed() {
  228. $this->account->expects($this->once())
  229. ->method('hasPermission')
  230. ->with('link to any page')
  231. ->willReturn(FALSE);
  232. $this->accessUnawareRouter->expects($this->never())
  233. ->method('match');
  234. $this->accessAwareRouter->expects($this->once())
  235. ->method('match')
  236. ->with('/test-path')
  237. ->willThrowException(new MethodNotAllowedException([]));
  238. $this->pathProcessor->expects($this->once())
  239. ->method('processInbound')
  240. ->willReturnArgument(0);
  241. $this->assertFalse($this->pathValidator->isValid('test-path'));
  242. }
  243. /**
  244. * Tests the isValid() method with a not working param converting.
  245. *
  246. * @covers ::isValid
  247. */
  248. public function testIsValidWithFailingParameterConverting() {
  249. $this->account->expects($this->once())
  250. ->method('hasPermission')
  251. ->with('link to any page')
  252. ->willReturn(FALSE);
  253. $this->accessUnawareRouter->expects($this->never())
  254. ->method('match');
  255. $this->accessAwareRouter->expects($this->once())
  256. ->method('match')
  257. ->with('/entity-test/1')
  258. ->willThrowException(new ParamNotConvertedException());
  259. $this->pathProcessor->expects($this->once())
  260. ->method('processInbound')
  261. ->willReturnArgument(0);
  262. $this->assertFalse($this->pathValidator->isValid('entity-test/1'));
  263. }
  264. /**
  265. * Tests the isValid() method with a not existing path.
  266. *
  267. * @covers ::isValid
  268. */
  269. public function testIsValidWithNotExistingPath() {
  270. $this->account->expects($this->once())
  271. ->method('hasPermission')
  272. ->with('link to any page')
  273. ->willReturn(FALSE);
  274. $this->accessUnawareRouter->expects($this->never())
  275. ->method('match');
  276. $this->accessAwareRouter->expects($this->once())
  277. ->method('match')
  278. ->with('/not-existing-path')
  279. ->willThrowException(new ResourceNotFoundException());
  280. $this->pathProcessor->expects($this->once())
  281. ->method('processInbound')
  282. ->willReturnArgument(0);
  283. $this->assertFalse($this->pathValidator->isValid('not-existing-path'));
  284. }
  285. /**
  286. * Tests the getUrlIfValid() method when there is access.
  287. *
  288. * @covers ::getUrlIfValid
  289. * @covers ::getPathAttributes
  290. */
  291. public function testGetUrlIfValidWithAccess() {
  292. $this->account->expects($this->exactly(2))
  293. ->method('hasPermission')
  294. ->with('link to any page')
  295. ->willReturn(FALSE);
  296. $this->accessAwareRouter->expects($this->exactly(2))
  297. ->method('match')
  298. ->with('/test-path')
  299. ->willReturn([RouteObjectInterface::ROUTE_NAME => 'test_route', '_raw_variables' => new ParameterBag(['key' => 'value'])]);
  300. $this->pathProcessor->expects($this->exactly(2))
  301. ->method('processInbound')
  302. ->willReturnArgument(0);
  303. $url = $this->pathValidator->getUrlIfValid('test-path');
  304. $this->assertInstanceOf('Drupal\Core\Url', $url);
  305. $this->assertEquals('test_route', $url->getRouteName());
  306. $this->assertEquals(['key' => 'value'], $url->getRouteParameters());
  307. // Test with leading /.
  308. $url = $this->pathValidator->getUrlIfValid('/test-path');
  309. $this->assertInstanceOf('Drupal\Core\Url', $url);
  310. $this->assertEquals('test_route', $url->getRouteName());
  311. $this->assertEquals(['key' => 'value'], $url->getRouteParameters());
  312. }
  313. /**
  314. * Tests the getUrlIfValid() method with a query in the path.
  315. *
  316. * @covers ::getUrlIfValid
  317. */
  318. public function testGetUrlIfValidWithQuery() {
  319. $this->account->expects($this->once())
  320. ->method('hasPermission')
  321. ->with('link to any page')
  322. ->willReturn(FALSE);
  323. $this->accessAwareRouter->expects($this->once())
  324. ->method('match')
  325. ->with('/test-path?k=bar')
  326. ->willReturn([RouteObjectInterface::ROUTE_NAME => 'test_route', '_raw_variables' => new ParameterBag()]);
  327. $this->pathProcessor->expects($this->once())
  328. ->method('processInbound')
  329. ->willReturnArgument(0);
  330. $url = $this->pathValidator->getUrlIfValid('test-path?k=bar');
  331. $this->assertInstanceOf('Drupal\Core\Url', $url);
  332. $this->assertEquals('test_route', $url->getRouteName());
  333. $this->assertEquals(['k' => 'bar'], $url->getOptions()['query']);
  334. }
  335. /**
  336. * Tests the getUrlIfValid() method where there is no access.
  337. *
  338. * @covers ::getUrlIfValid
  339. */
  340. public function testGetUrlIfValidWithoutAccess() {
  341. $this->account->expects($this->once())
  342. ->method('hasPermission')
  343. ->with('link to any page')
  344. ->willReturn(FALSE);
  345. $this->accessAwareRouter->expects($this->once())
  346. ->method('match')
  347. ->with('/test-path')
  348. ->willThrowException(new AccessDeniedHttpException());
  349. $this->pathProcessor->expects($this->once())
  350. ->method('processInbound')
  351. ->willReturnArgument(0);
  352. $url = $this->pathValidator->getUrlIfValid('test-path');
  353. $this->assertFalse($url);
  354. }
  355. /**
  356. * Tests the getUrlIfValid() method with a front page + query + fragments.
  357. *
  358. * @covers ::getUrlIfValid
  359. */
  360. public function testGetUrlIfValidWithFrontPageAndQueryAndFragments() {
  361. $url = $this->pathValidator->getUrlIfValid('<front>?hei=sen#berg');
  362. $this->assertEquals('<front>', $url->getRouteName());
  363. $this->assertEquals(['hei' => 'sen'], $url->getOptions()['query']);
  364. $this->assertEquals('berg', $url->getOptions()['fragment']);
  365. }
  366. /**
  367. * Tests the getUrlIfValidWithoutAccessCheck() method.
  368. *
  369. * @covers ::getUrlIfValidWithoutAccessCheck
  370. * @covers ::getPathAttributes
  371. */
  372. public function testGetUrlIfValidWithoutAccessCheck() {
  373. $this->account->expects($this->never())
  374. ->method('hasPermission')
  375. ->with('link to any page');
  376. $this->accessAwareRouter->expects($this->never())
  377. ->method('match');
  378. $this->accessUnawareRouter->expects($this->once())
  379. ->method('match')
  380. ->with('/test-path')
  381. ->willReturn([RouteObjectInterface::ROUTE_NAME => 'test_route', '_raw_variables' => new ParameterBag(['key' => 'value'])]);
  382. $this->pathProcessor->expects($this->once())
  383. ->method('processInbound')
  384. ->willReturnArgument(0);
  385. $url = $this->pathValidator->getUrlIfValidWithoutAccessCheck('test-path');
  386. $this->assertInstanceOf('Drupal\Core\Url', $url);
  387. $this->assertEquals('test_route', $url->getRouteName());
  388. $this->assertEquals(['key' => 'value'], $url->getRouteParameters());
  389. }
  390. }