summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Pott2015-02-27 13:13:07 (GMT)
committerAlex Pott2015-02-27 13:13:07 (GMT)
commit1a728480f5d620adec7352d2a05449909150eecd (patch)
treed9bde0c15ff11d142bb7a6c23f73ff2cb9d285db
parent9c2b19cdaa075dfce3a15010d3d6c22aff9816ed (diff)
Issue #2418181 by dawehner, Wim Leers, effulgentsia: Remove 404 validation from link creation
-rw-r--r--core/modules/link/src/Plugin/Field/FieldType/LinkItem.php2
-rw-r--r--core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php25
-rw-r--r--core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraint.php24
-rw-r--r--core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidator.php54
-rw-r--r--core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraint.php24
-rw-r--r--core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidator.php63
-rw-r--r--core/modules/link/src/Tests/LinkFieldTest.php12
-rw-r--r--core/modules/link/tests/src/Plugin/Validation/Constraint/LinkAccessConstraintValidatorTest.php (renamed from core/modules/link/tests/src/LinkAccessConstraintValidatorTest.php)4
-rw-r--r--core/modules/link/tests/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidatorTest.php116
-rw-r--r--core/modules/link/tests/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidatorTest.php114
-rw-r--r--core/modules/menu_ui/src/Tests/MenuTest.php9
11 files changed, 409 insertions, 38 deletions
diff --git a/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php b/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
index 9f65cae..a552a0e 100644
--- a/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
+++ b/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
@@ -27,7 +27,7 @@ use Drupal\link\LinkItemInterface;
* description = @Translation("Stores a URL string, optional varchar link text, and optional blob of attributes to assemble a link."),
* default_widget = "link_default",
* default_formatter = "link",
- * constraints = {"LinkType" = {}, "LinkAccess" = {}}
+ * constraints = {"LinkType" = {}, "LinkAccess" = {}, "LinkExternalProtocols" = {}, "LinkNotExistingInternal" = {}}
* )
*/
class LinkItem extends FieldItemBase implements LinkItemInterface {
diff --git a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
index f7168c3..f5a5353 100644
--- a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
+++ b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
@@ -147,31 +147,6 @@ class LinkWidget extends WidgetBase {
$form_state->setError($element, t('Manually entered paths should start with /, ? or #.'));
return;
}
-
- // If the URI is empty or not well-formed, the link field type's validation
- // constraint will detect it.
- // @see \Drupal\link\Plugin\Validation\Constraint\LinkTypeConstraint::validate()
- if (!empty($uri) && parse_url($uri)) {
- $url = Url::fromUri($uri);
-
- // Disallow unrouted internal URLs (i.e. disallow 'base:' URIs).
- $disallowed = !$url->isRouted() && !$url->isExternal();
- // Disallow external URLs using untrusted protocols.
- $disallowed = $disallowed || ($url->isExternal() && !in_array(parse_url($uri, PHP_URL_SCHEME), UrlHelper::getAllowedProtocols()));
- // Disallow routed URLs that don't exist.
- if (!$disallowed && $url->isRouted()) {
- try {
- $url->toString();
- }
- catch (RouteNotFoundException $e) {
- $disallowed = TRUE;
- }
- }
-
- if ($disallowed) {
- $form_state->setError($element, t("The path '@link_path' is invalid.", ['@link_path' => static::getUriAsDisplayableString($uri)]));
- }
- }
}
/**
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraint.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraint.php
new file mode 100644
index 0000000..881c66e
--- /dev/null
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraint.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\link\Plugin\Validation\Constraint\LinkExternalProtocols.
+ */
+
+namespace Drupal\link\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+
+/**
+ * Defines a protocol validation constraint for links to external URLs.
+ *
+ * @Plugin(
+ * id = "LinkExternalProtocols",
+ * label = @Translation("No dangerous external protocols", context = "Validation"),
+ * )
+ */
+class LinkExternalProtocolsConstraint extends Constraint {
+
+ public $message = "The path '@uri' is invalid.";
+
+}
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidator.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidator.php
new file mode 100644
index 0000000..7aa8f75
--- /dev/null
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidator.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\link\Plugin\Validation\Constraint\LinkExternalProtocolsValidator.
+ */
+
+namespace Drupal\link\Plugin\Validation\Constraint;
+
+use Drupal\Component\Utility\UrlHelper;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidatorInterface;
+use Symfony\Component\Validator\ExecutionContextInterface;
+
+/**
+ * Validates the LinkExternalProtocols constraint.
+ */
+class LinkExternalProtocolsConstraintValidator implements ConstraintValidatorInterface {
+
+ /**
+ * Stores the validator's state during validation.
+ *
+ * @var \Symfony\Component\Validator\ExecutionContextInterface
+ */
+ protected $context;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(ExecutionContextInterface $context) {
+ $this->context = $context;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validate($value, Constraint $constraint) {
+ if (isset($value)) {
+ try {
+ /** @var \Drupal\Core\Url $url */
+ $url = $value->getUrl();
+ }
+ // If the URL is malformed this constraint cannot check further.
+ catch (\InvalidArgumentException $e) {
+ return;
+ }
+ // Disallow external URLs using untrusted protocols.
+ if ($url->isExternal() && !in_array(parse_url($url->getUri(), PHP_URL_SCHEME), UrlHelper::getAllowedProtocols())) {
+ $this->context->addViolation($constraint->message, array('@uri' => $value->uri));
+ }
+ }
+ }
+
+}
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraint.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraint.php
new file mode 100644
index 0000000..3bcbb98
--- /dev/null
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraint.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\link\Plugin\Validation\Constraint\LinkNotExistingInternal.
+ */
+
+namespace Drupal\link\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+
+/**
+ * Defines a protocol validation constraint for links to broken internal URLs.
+ *
+ * @Plugin(
+ * id = "LinkNotExistingInternal",
+ * label = @Translation("No broken internal links", context = "Validation"),
+ * )
+ */
+class LinkNotExistingInternalConstraint extends Constraint {
+
+ public $message = "The path '@uri' is invalid.";
+
+}
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidator.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidator.php
new file mode 100644
index 0000000..dad78ab
--- /dev/null
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidator.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\link\Plugin\Validation\Constraint\LinkNotExistingInternalConstraintValidator.
+ */
+
+namespace Drupal\link\Plugin\Validation\Constraint;
+
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidatorInterface;
+use Symfony\Component\Validator\ExecutionContextInterface;
+
+/**
+ * Validates the LinkNotExistingInternal constraint.
+ */
+class LinkNotExistingInternalConstraintValidator implements ConstraintValidatorInterface {
+
+ /**
+ * Stores the validator's state during validation.
+ *
+ * @var \Symfony\Component\Validator\ExecutionContextInterface
+ */
+ protected $context;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(ExecutionContextInterface $context) {
+ $this->context = $context;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validate($value, Constraint $constraint) {
+ if (isset($value)) {
+ try {
+ /** @var \Drupal\Core\Url $url */
+ $url = $value->getUrl();
+ }
+ // If the URL is malformed this constraint cannot check further.
+ catch (\InvalidArgumentException $e) {
+ return;
+ }
+
+ if ($url->isRouted()) {
+ $allowed = TRUE;
+ try {
+ $url->toString();
+ }
+ catch (RouteNotFoundException $e) {
+ $allowed = FALSE;
+ }
+ if (!$allowed) {
+ $this->context->addViolation($constraint->message, array('@uri' => $value->uri));
+ }
+ }
+ }
+ }
+
+}
diff --git a/core/modules/link/src/Tests/LinkFieldTest.php b/core/modules/link/src/Tests/LinkFieldTest.php
index 28bd87c..419fade 100644
--- a/core/modules/link/src/Tests/LinkFieldTest.php
+++ b/core/modules/link/src/Tests/LinkFieldTest.php
@@ -136,16 +136,14 @@ class LinkFieldTest extends WebTestBase {
// Define some invalid URLs.
$validation_error_1 = "The path '@link_path' is invalid.";
$validation_error_2 = 'Manually entered paths should start with /, ? or #.';
+ $validation_error_3 = "The path '@link_path' is inaccessible.";
$invalid_external_entries = array(
- // Missing protcol
- 'not-an-url' => $validation_error_2,
// Invalid protocol
'invalid://not-a-valid-protocol' => $validation_error_1,
// Missing host name
'http://' => $validation_error_1,
);
$invalid_internal_entries = array(
- '/non/existing/path' => $validation_error_1,
'no-leading-slash' => $validation_error_2,
'entity:non_existing_entity_type/yar' => $validation_error_1,
);
@@ -165,6 +163,14 @@ class LinkFieldTest extends WebTestBase {
$this->field->save();
$this->assertValidEntries($field_name, $valid_internal_entries);
$this->assertInvalidEntries($field_name, $valid_external_entries + $invalid_internal_entries);
+
+ // Ensure that users with 'link to any page', don't apply access checking.
+ $this->drupalLogin($this->drupalCreateUser([
+ 'view test entity',
+ 'administer entity_test content',
+ ]));
+ $this->assertValidEntries($field_name, ['/entity_test/add' => '/entity_test/add']);
+ $this->assertInValidEntries($field_name, ['/admin' => $validation_error_3]);
}
/**
diff --git a/core/modules/link/tests/src/LinkAccessConstraintValidatorTest.php b/core/modules/link/tests/src/Plugin/Validation/Constraint/LinkAccessConstraintValidatorTest.php
index 899d07f..a3711ec 100644
--- a/core/modules/link/tests/src/LinkAccessConstraintValidatorTest.php
+++ b/core/modules/link/tests/src/Plugin/Validation/Constraint/LinkAccessConstraintValidatorTest.php
@@ -2,10 +2,10 @@
/**
* @file
- * Contains \Drupal\Tests\link\LinkAccessConstraintValidatorTest.
+ * Contains \Drupal\Tests\link\Plugin\Validation\Constraint\LinkAccessConstraintValidatorTest.
*/
-namespace Drupal\Tests\link;
+namespace Drupal\Tests\link\Plugin\Validation\Constraint;
use Drupal\Core\Url;
use Drupal\link\Plugin\Validation\Constraint\LinkAccessConstraint;
diff --git a/core/modules/link/tests/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidatorTest.php b/core/modules/link/tests/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidatorTest.php
new file mode 100644
index 0000000..2b92971
--- /dev/null
+++ b/core/modules/link/tests/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidatorTest.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\link\Plugin\Validation\Constraint\LinkExternalProtocolsConstraintValidatorTest.
+ */
+
+namespace Drupal\Tests\link\Plugin\Validation\Constraint;
+
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Url;
+use Drupal\link\Plugin\Validation\Constraint\LinkExternalProtocolsConstraint;
+use Drupal\link\Plugin\Validation\Constraint\LinkExternalProtocolsConstraintValidator;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\link\Plugin\Validation\Constraint\LinkExternalProtocolsConstraintValidator
+ * @group Link
+ */
+class LinkExternalProtocolsConstraintValidatorTest extends UnitTestCase {
+
+ /**
+ * @covers ::validate
+ * @dataProvider providerValidate
+ */
+ public function testValidate($value, $valid) {
+ $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+
+ if ($valid) {
+ $context->expects($this->never())
+ ->method('addViolation');
+ }
+ else {
+ $context->expects($this->once())
+ ->method('addViolation');
+ }
+
+ // Setup some more allowed protocols.
+ UrlHelper::setAllowedProtocols(['http', 'https', 'magnet']);
+
+ $constraint = new LinkExternalProtocolsConstraint();
+
+ $validator = new LinkExternalProtocolsConstraintValidator();
+ $validator->initialize($context);
+ $validator->validate($value, $constraint);
+ }
+
+ /**
+ * Data provider for ::testValidate
+ */
+ public function providerValidate() {
+ $data = [];
+
+ // Test allowed protocols.
+ $data[] = ['http://www.drupal.org', TRUE];
+ $data[] = ['https://www.drupal.org', TRUE];
+ $data[] = ['magnet:?xt=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C', TRUE];
+
+ // Invalid protocols.
+ $data[] = ['ftp://ftp.funet.fi/pub/standards/RFC/rfc959.txt', FALSE];
+
+ foreach ($data as &$single_data) {
+ $url = Url::fromUri($single_data[0]);
+ $link = $this->getMock('Drupal\link\LinkItemInterface');
+ $link->expects($this->any())
+ ->method('getUrl')
+ ->willReturn($url);
+ $single_data[0] = $link;
+ }
+
+ return $data;
+ }
+
+ /**
+ * @covers ::validate
+ *
+ * @see \Drupal\Core\Url::fromUri
+ */
+ public function testValidateWithMalformedUri() {
+ $link = $this->getMock('Drupal\link\LinkItemInterface');
+ $link->expects($this->any())
+ ->method('getUrl')
+ ->willThrowException(new \InvalidArgumentException());
+
+ $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+ $context->expects($this->never())
+ ->method('addViolation');
+
+ $constraint = new LinkExternalProtocolsConstraint();
+
+ $validator = new LinkExternalProtocolsConstraintValidator();
+ $validator->initialize($context);
+ $validator->validate($link, $constraint);
+ }
+
+ /**
+ * @covers ::validate
+ */
+ public function testValidateIgnoresInternalUrls() {
+ $link = $this->getMock('Drupal\link\LinkItemInterface');
+ $link->expects($this->any())
+ ->method('getUrl')
+ ->willReturn(Url::fromRoute('example.test'));
+
+ $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+ $context->expects($this->never())
+ ->method('addViolation');
+
+ $constraint = new LinkExternalProtocolsConstraint();
+
+ $validator = new LinkExternalProtocolsConstraintValidator();
+ $validator->initialize($context);
+ $validator->validate($link, $constraint);
+ }
+
+}
diff --git a/core/modules/link/tests/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidatorTest.php b/core/modules/link/tests/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidatorTest.php
new file mode 100644
index 0000000..545437c
--- /dev/null
+++ b/core/modules/link/tests/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidatorTest.php
@@ -0,0 +1,114 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\link\Plugin\Validation\Constraint\LinkNotExistingInternalConstraintValidatorTest.
+ */
+
+namespace Drupal\Tests\link\Plugin\Validation\Constraint;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Url;
+use Drupal\link\Plugin\Validation\Constraint\LinkNotExistingInternalConstraint;
+use Drupal\link\Plugin\Validation\Constraint\LinkNotExistingInternalConstraintValidator;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+
+/**
+ * @coversDefaultClass \Drupal\link\Plugin\Validation\Constraint\LinkNotExistingInternalConstraintValidator
+ * @group Link
+ */
+class LinkNotExistingInternalConstraintValidatorTest extends UnitTestCase {
+
+ /**
+ * @covers ::validate
+ * @dataProvider providerValidate
+ */
+ public function testValidate($value, $valid) {
+ $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+
+ if ($valid) {
+ $context->expects($this->never())
+ ->method('addViolation');
+ }
+ else {
+ $context->expects($this->once())
+ ->method('addViolation');
+ }
+
+
+ $constraint = new LinkNotExistingInternalConstraint();
+
+ $validator = new LinkNotExistingInternalConstraintValidator();
+ $validator->initialize($context);
+ $validator->validate($value, $constraint);
+ }
+
+ /**
+ * Data provider for ::testValidate
+ */
+ public function providerValidate() {
+ $data = [];
+
+ // External URL
+ $data[] = [Url::fromUri('https://www.drupal.org'), TRUE];
+
+ // Existing routed URL.
+ $url = Url::fromRoute('example.existing_route');
+
+ $url_generator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');
+ $url_generator->expects($this->any())
+ ->method('generateFromRoute')
+ ->with('example.existing_route', [], [])
+ ->willReturn('/example/existing');
+ $url->setUrlGenerator($url_generator);
+
+ $data[] = [$url, TRUE];
+
+ // Not existing routed URL.
+ $url = Url::fromRoute('example.not_existing_route');
+
+ $url_generator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');
+ $url_generator->expects($this->any())
+ ->method('generateFromRoute')
+ ->with('example.not_existing_route', [], [])
+ ->willThrowException(new RouteNotFoundException());
+ $url->setUrlGenerator($url_generator);
+
+ $data[] = [$url, FALSE];
+
+ foreach ($data as &$single_data) {
+ $link = $this->getMock('Drupal\link\LinkItemInterface');
+ $link->expects($this->any())
+ ->method('getUrl')
+ ->willReturn($single_data[0]);
+
+ $single_data[0] = $link;
+ }
+
+ return $data;
+ }
+
+ /**
+ * @covers ::validate
+ *
+ * @see \Drupal\Core\Url::fromUri
+ */
+ public function testValidateWithMalformedUri() {
+ $link = $this->getMock('Drupal\link\LinkItemInterface');
+ $link->expects($this->any())
+ ->method('getUrl')
+ ->willThrowException(new \InvalidArgumentException());
+
+ $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+ $context->expects($this->never())
+ ->method('addViolation');
+
+ $constraint = new LinkNotExistingInternalConstraint();
+
+ $validator = new LinkNotExistingInternalConstraintValidator();
+ $validator->initialize($context);
+ $validator->validate($link, $constraint);
+ }
+
+}
diff --git a/core/modules/menu_ui/src/Tests/MenuTest.php b/core/modules/menu_ui/src/Tests/MenuTest.php
index 71bfd73..5cb6563 100644
--- a/core/modules/menu_ui/src/Tests/MenuTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuTest.php
@@ -639,18 +639,13 @@ class MenuTest extends MenuWebTestBase {
* Attempts to add menu link with invalid path or no access permission.
*/
function addInvalidMenuLink() {
- foreach (array('valid' => '/-&-', 'access' => '/admin/people/permissions') as $type => $link_path) {
+ foreach (array('access' => '/admin/people/permissions') as $type => $link_path) {
$edit = array(
'link[0][uri]' => $link_path,
'title[0][value]' => 'title',
);
$this->drupalPostForm("admin/structure/menu/manage/{$this->menu->id()}/add", $edit, t('Save'));
- if ($type === 'access') {
- $this->assertRaw(t("The path '@link_path' is inaccessible.", array('@link_path' => $link_path)), 'Menu link was not created');
- }
- else {
- $this->assertRaw(t("The path '@link_path' is invalid.", array('@link_path' => $link_path)), 'Menu link was not created');
- }
+ $this->assertRaw(t("The path '@link_path' is inaccessible.", array('@link_path' => $link_path)), 'Menu link was not created');
}
}