Example #1
0
 /**
  * {@inheritdoc}
  */
 public function execute($object = NULL)
 {
     $url = $this->configuration['url'];
     // Leave external URLs unchanged, and assemble others as absolute URLs
     // relative to the site's base URL.
     if (!UrlHelper::isExternal($url)) {
         $parts = UrlHelper::parse($url);
         // @todo '<front>' is valid input for BC reasons, may be removed by
         //   https://www.drupal.org/node/2421941
         if ($parts['path'] === '<front>') {
             $parts['path'] = '';
         }
         $uri = 'base:' . $parts['path'];
         $options = ['query' => $parts['query'], 'fragment' => $parts['fragment'], 'absolute' => TRUE];
         // Treat this as if it's user input of a path relative to the site's
         // base URL.
         $url = $this->unroutedUrlAssembler->assemble($uri, $options);
     }
     $response = new RedirectResponse($url);
     $listener = function ($event) use($response) {
         $event->setResponse($response);
     };
     // Add the listener to the event dispatcher.
     $this->dispatcher->addListener(KernelEvents::RESPONSE, $listener);
 }
Example #2
0
 /**
  * {@inheritdoc}
  */
 public function isValid($path)
 {
     // External URLs and the front page are always valid.
     if ($path == '<front>' || UrlHelper::isExternal($path)) {
         return TRUE;
     }
     // Check the routing system.
     $collection = $this->routeProvider->getRoutesByPattern('/' . $path);
     if ($collection->count() == 0) {
         return FALSE;
     }
     $request = RequestHelper::duplicate($this->requestStack->getCurrentRequest(), '/' . $path);
     $request->attributes->set('_system_path', $path);
     // We indicate that a menu administrator is running the menu access check.
     $request->attributes->set('_menu_admin', TRUE);
     // Attempt to match this path to provide a fully built request to the
     // access checker.
     try {
         $request->attributes->add($this->requestMatcher->matchRequest($request));
     } catch (ParamNotConvertedException $e) {
         return FALSE;
     }
     // Consult the access manager.
     $routes = $collection->all();
     $route = reset($routes);
     return $this->accessManager->check($route, $request, $this->account);
 }
 /**
  * {@inheritdoc}
  *
  * This is a helper function that calls buildExternalUrl() or buildLocalUrl()
  * based on a check of whether the path is a valid external URL.
  */
 public function assemble($uri, array $options = [])
 {
     // Note that UrlHelper::isExternal will return FALSE if the $uri has a
     // disallowed protocol.  This is later made safe since we always add at
     // least a leading slash.
     if (strpos($uri, 'base://') === 0) {
         return $this->buildLocalUrl($uri, $options);
     } elseif (UrlHelper::isExternal($uri)) {
         // UrlHelper::isExternal() only returns true for safe protocols.
         return $this->buildExternalUrl($uri, $options);
     }
     throw new \InvalidArgumentException(String::format('The URI "@uri" is invalid. You must use a valid URI scheme. Use base:// for a path, e.g., to a Drupal file that needs the base path. Do not use this for internal paths controlled by Drupal.', ['@uri' => $uri]));
 }
 /**
  * {@inheritdoc}
  *
  * This is a helper function that calls buildExternalUrl() or buildLocalUrl()
  * based on a check of whether the path is a valid external URL.
  */
 public function assemble($uri, array $options = [], $collect_bubbleable_metadata = FALSE)
 {
     // Note that UrlHelper::isExternal will return FALSE if the $uri has a
     // disallowed protocol.  This is later made safe since we always add at
     // least a leading slash.
     if (parse_url($uri, PHP_URL_SCHEME) === 'base') {
         return $this->buildLocalUrl($uri, $options, $collect_bubbleable_metadata);
     } elseif (UrlHelper::isExternal($uri)) {
         // UrlHelper::isExternal() only returns true for safe protocols.
         return $this->buildExternalUrl($uri, $options, $collect_bubbleable_metadata);
     }
     throw new \InvalidArgumentException("The URI '{$uri}' is invalid. You must use a valid URI scheme. Use base: for a path, e.g., to a Drupal file that needs the base path. Do not use this for internal paths controlled by Drupal.");
 }
Example #5
0
 /**
  * {@inheritdoc}
  */
 public function get()
 {
     if (!isset($this->destination)) {
         $query = $this->requestStack->getCurrentRequest()->query;
         if (UrlHelper::isExternal($query->get('destination'))) {
             $this->destination = '/';
         } elseif ($query->has('destination')) {
             $this->destination = $query->get('destination');
         } else {
             $this->destination = $this->urlGenerator->generateFromRoute('<current>', [], ['query' => UrlHelper::buildQuery(UrlHelper::filterQueryParameters($query->all()))]);
         }
     }
     return $this->destination;
 }
 /**
  * Allows manipulation of the response object when performing a redirect.
  *
  * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
  *   The Event to process.
  */
 public function checkRedirectUrl(FilterResponseEvent $event)
 {
     $response = $event->getResponse();
     if ($response instanceof RedirectResponse) {
         $options = array();
         $destination = $event->getRequest()->query->get('destination');
         // A destination from \Drupal::request()->query always overrides the
         // current RedirectResponse. We do not allow absolute URLs to be passed
         // via \Drupal::request()->query, as this can be an attack vector, with
         // the following exception:
         // - Absolute URLs that point to this site (i.e. same base URL and
         //   base path) are allowed.
         if ($destination && (!UrlHelper::isExternal($destination) || UrlHelper::externalIsLocal($destination, $GLOBALS['base_url']))) {
             $destination = UrlHelper::parse($destination);
             $path = $destination['path'];
             $options['query'] = $destination['query'];
             $options['fragment'] = $destination['fragment'];
             // The 'Location' HTTP header must always be absolute.
             $options['absolute'] = TRUE;
             $response->setTargetUrl($this->urlGenerator->generateFromPath($path, $options));
         }
     }
 }
 /**
  * {@inheritdoc}
  */
 public function process($text, $langcode)
 {
     $result = new FilterProcessResult($text);
     $dom = Html::load($text);
     $xpath = new \DOMXPath($dom);
     /** @var \DOMNode $node */
     foreach ($xpath->query('//img') as $node) {
         // Read the data-align attribute's value, then delete it.
         $width = $node->getAttribute('width');
         $height = $node->getAttribute('height');
         $src = $node->getAttribute('src');
         if (!UrlHelper::isExternal($src)) {
             if ($width || $height) {
                 /** @var \DOMNode $element */
                 $element = $dom->createElement('a');
                 $element->setAttribute('href', $src);
                 $node->parentNode->replaceChild($element, $node);
                 $element->appendChild($node);
             }
         }
     }
     $result->setProcessedText(Html::serialize($dom));
     return $result;
 }
Example #8
0
 /**
  * {@inheritdoc}
  */
 public function massageFormValues(array $values, array $form, array &$form_state)
 {
     foreach ($values as &$value) {
         if (!empty($value['url'])) {
             try {
                 $parsed_url = UrlHelper::parse($value['url']);
                 // If internal links are supported, look up whether the given value is
                 // a path alias and store the system path instead.
                 if ($this->supportsInternalLinks() && !UrlHelper::isExternal($value['url'])) {
                     $parsed_url['path'] = \Drupal::service('path.alias_manager')->getPathByAlias($parsed_url['path']);
                 }
                 $url = Url::createFromPath($parsed_url['path']);
                 $url->setOption('query', $parsed_url['query']);
                 $url->setOption('fragment', $parsed_url['fragment']);
                 $url->setOption('attributes', $value['attributes']);
                 $value += $url->toArray();
                 // Reset the URL value to contain only the path.
                 $value['url'] = $parsed_url['path'];
             } catch (NotFoundHttpException $e) {
                 // Nothing to do here, LinkTypeConstraintValidator emits errors.
             } catch (MatchingRouteNotFoundException $e) {
                 // Nothing to do here, LinkTypeConstraintValidator emits errors.
             } catch (ParamNotConvertedException $e) {
                 // Nothing to do here, LinkTypeConstraintValidator emits errors.
             }
         }
     }
     return $values;
 }
Example #9
0
 /**
  * Helper for getUrlIfValid() and getUrlIfValidWithoutAccessCheck().
  */
 protected function getUrl($path, $access_check)
 {
     $path = ltrim($path, '/');
     $parsed_url = UrlHelper::parse($path);
     $options = [];
     if (!empty($parsed_url['query'])) {
         $options['query'] = $parsed_url['query'];
     }
     if (!empty($parsed_url['fragment'])) {
         $options['fragment'] = $parsed_url['fragment'];
     }
     if ($parsed_url['path'] == '<front>') {
         return new Url('<front>', [], $options);
     } elseif ($parsed_url['path'] == '<none>') {
         return new Url('<none>', [], $options);
     } elseif (UrlHelper::isExternal($path) && UrlHelper::isValid($path)) {
         if (empty($parsed_url['path'])) {
             return FALSE;
         }
         return Url::fromUri($path);
     }
     $request = Request::create('/' . $path);
     $attributes = $this->getPathAttributes($path, $request, $access_check);
     if (!$attributes) {
         return FALSE;
     }
     $route_name = $attributes[RouteObjectInterface::ROUTE_NAME];
     $route_parameters = $attributes['_raw_variables']->all();
     return new Url($route_name, $route_parameters, $options + ['query' => $request->query->all()]);
 }
 /**
  * {@inheritdoc}
  */
 public function massageFormValues(array $values, array $form, FormStateInterface $form_state)
 {
     global $base_url;
     $values = parent::massageFormValues($values, $form, $form_state);
     $file_urls = [];
     $countable_fields = $this->getSetting('file_fields');
     foreach ($countable_fields as $field) {
         $files_values = array_filter(array_column($form_state->getValue($field), 'fids'));
         foreach ($files_values as $file_value) {
             /** @var FileInterface $file */
             $file = File::load(reset($file_value));
             if ($file) {
                 $file_urls[] = $file->url();
             }
         }
     }
     // Remove removed files from access urls.
     foreach ($values as $delta => $value) {
         if (UrlHelper::isExternal($value['uri']) && UrlHelper::externalIsLocal($value['uri'], $base_url) && !in_array($value['uri'], $file_urls)) {
             unset($values[$delta]);
         }
     }
     // Add new or updated files to the access urls.
     foreach ($file_urls as $file_url) {
         if (!array_search($file_url, array_column($values, 'uri'))) {
             $values[]['uri'] = $file_url;
         }
     }
     return $values;
 }
Example #11
0
 /**
  * Overrides EntityForm::validate().
  */
 public function validate(array $form, array &$form_state)
 {
     $menu_link = $this->buildEntity($form, $form_state);
     $normal_path = $this->pathAliasManager->getPathByAlias($menu_link->link_path);
     if ($menu_link->link_path != $normal_path) {
         drupal_set_message(t('The menu system stores system paths only, but will use the URL alias for display. %link_path has been stored as %normal_path', array('%link_path' => $menu_link->link_path, '%normal_path' => $normal_path)));
         $menu_link->link_path = $normal_path;
         $form_state['values']['link_path'] = $normal_path;
     }
     if (!UrlHelper::isExternal($menu_link->link_path)) {
         $parsed_link = parse_url($menu_link->link_path);
         if (isset($parsed_link['query'])) {
             $menu_link->options['query'] = array();
             parse_str($parsed_link['query'], $menu_link->options['query']);
         } else {
             // Use unset() rather than setting to empty string
             // to avoid redundant serialized data being stored.
             unset($menu_link->options['query']);
         }
         if (isset($parsed_link['fragment'])) {
             $menu_link->options['fragment'] = $parsed_link['fragment'];
         } else {
             unset($menu_link->options['fragment']);
         }
         if (isset($parsed_link['path']) && $menu_link->link_path != $parsed_link['path']) {
             $menu_link->link_path = $parsed_link['path'];
         }
     }
     if (!trim($menu_link->link_path) || !drupal_valid_path($menu_link->link_path, TRUE)) {
         $this->setFormError('link_path', $form_state, $this->t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $menu_link->link_path)));
     }
     parent::validate($form, $form_state);
 }
 /**
  * Breaks up a user-entered URL or path into all the relevant parts.
  *
  * @param string $url
  *   The user-entered URL or path.
  *
  * @return array
  *   The extracted parts.
  */
 protected function extractUrl($url)
 {
     $extracted = UrlHelper::parse($url);
     $external = UrlHelper::isExternal($url);
     if ($external) {
         $extracted['url'] = $extracted['path'];
         $extracted['route_name'] = NULL;
         $extracted['route_parameters'] = [];
     } else {
         $extracted['url'] = '';
         // If the path doesn't match a Drupal path, the route should end up empty.
         $extracted['route_name'] = NULL;
         $extracted['route_parameters'] = [];
         try {
             // Find the route_name.
             $url_obj = \Drupal::pathValidator()->getUrlIfValid($extracted['path']);
             if ($url_obj) {
                 $extracted['route_name'] = $url_obj->getRouteName();
                 $extracted['route_parameters'] = $url_obj->getRouteParameters();
             }
         } catch (MatchingRouteNotFoundException $e) {
             // The path doesn't match a Drupal path.
         } catch (ParamNotConvertedException $e) {
             // A path like node/99 matched a route, but the route parameter was
             // invalid (e.g. node with ID 99 does not exist).
         }
     }
     return $extracted;
 }
Example #13
0
 /**
  * Tests UrlHelper::parse().
  */
 function testDrupalParseUrl()
 {
     // Relative, absolute, and external URLs, without/with explicit script path,
     // without/with Drupal path.
     foreach (array('', '/', 'http://drupal.org/') as $absolute) {
         foreach (array('', 'index.php/') as $script) {
             foreach (array('', 'foo/bar') as $path) {
                 $url = $absolute . $script . $path . '?foo=bar&bar=baz&baz#foo';
                 $expected = array('path' => $absolute . $script . $path, 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''), 'fragment' => 'foo');
                 $this->assertEqual(UrlHelper::parse($url), $expected, 'URL parsed correctly.');
             }
         }
     }
     // Relative URL that is known to confuse parse_url().
     $url = 'foo/bar:1';
     $result = array('path' => 'foo/bar:1', 'query' => array(), 'fragment' => '');
     $this->assertEqual(UrlHelper::parse($url), $result, 'Relative URL parsed correctly.');
     // Test that drupal can recognize an absolute URL. Used to prevent attack vectors.
     $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo';
     $this->assertTrue(UrlHelper::isExternal($url), 'Correctly identified an external URL.');
     // Test that UrlHelper::parse() does not allow spoofing a URL to force a malicious redirect.
     $parts = UrlHelper::parse('forged:http://cwe.mitre.org/data/definitions/601.html');
     $this->assertFalse(UrlHelper::isValid($parts['path'], TRUE), '\\Drupal\\Component\\Utility\\UrlHelper::isValid() correctly parsed a forged URL.');
 }
 /**
  * Breaks up a user-entered URL or path into all the relevant parts.
  *
  * @param string $url
  *   The user-entered URL or path.
  *
  * @return array
  *   The extracted parts.
  */
 protected function extractUrl($url)
 {
     $extracted = UrlHelper::parse($url);
     $external = UrlHelper::isExternal($url);
     if ($external) {
         $extracted['url'] = $extracted['path'];
         $extracted['route_name'] = NULL;
         $extracted['route_parameters'] = array();
     } else {
         $extracted['url'] = '';
         // If the path doesn't match a Drupal path, the route should end up empty.
         $extracted['route_name'] = NULL;
         $extracted['route_parameters'] = array();
         try {
             // Find the route_name.
             $normal_path = $this->pathAliasManager->getPathByAlias($extracted['path']);
             $url_obj = Url::createFromPath($normal_path);
             $extracted['route_name'] = $url_obj->getRouteName();
             $extracted['route_parameters'] = $url_obj->getRouteParameters();
         } catch (MatchingRouteNotFoundException $e) {
             // The path doesn't match a Drupal path.
         } catch (ParamNotConvertedException $e) {
             // A path like node/99 matched a route, but the route parameter was
             // invalid (e.g. node with ID 99 does not exist).
         }
     }
     return $extracted;
 }
  /**
   * {@inheritdoc}
   */
  public function renderText($alter) {
    // We need to preserve the safeness of the value regardless of the
    // alterations made by this method. Any alterations or replacements made
    // within this method need to ensure that at the minimum the result is
    // XSS admin filtered. See self::renderAltered() as an example that does.
    $value_is_safe = $this->last_render instanceof MarkupInterface;
    // Cast to a string so that empty checks and string functions work as
    // expected.
    $value = (string) $this->last_render;

    if (!empty($alter['alter_text']) && $alter['text'] !== '') {
      $tokens = $this->getRenderTokens($alter);
      $value = $this->renderAltered($alter, $tokens);
    }

    if (!empty($this->options['alter']['trim_whitespace'])) {
      $value = trim($value);
    }

    // Check if there should be no further rewrite for empty values.
    $no_rewrite_for_empty = $this->options['hide_alter_empty'] && $this->isValueEmpty($this->original_value, $this->options['empty_zero']);

    // Check whether the value is empty and return nothing, so the field isn't rendered.
    // First check whether the field should be hidden if the value(hide_alter_empty = TRUE) /the rewrite is empty (hide_alter_empty = FALSE).
    // For numeric values you can specify whether "0"/0 should be empty.
    if ((($this->options['hide_empty'] && empty($value))
        || ($alter['phase'] != static::RENDER_TEXT_PHASE_EMPTY && $no_rewrite_for_empty))
      && $this->isValueEmpty($value, $this->options['empty_zero'], FALSE)) {
      return '';
    }
    // Only in empty phase.
    if ($alter['phase'] == static::RENDER_TEXT_PHASE_EMPTY && $no_rewrite_for_empty) {
      // If we got here then $alter contains the value of "No results text"
      // and so there is nothing left to do.
      return ViewsRenderPipelineMarkup::create($value);
    }

    if (!empty($alter['strip_tags'])) {
      $value = strip_tags($value, $alter['preserve_tags']);
    }

    $more_link = '';
    if (!empty($alter['trim']) && !empty($alter['max_length'])) {
      $length = strlen($value);
      $value = $this->renderTrimText($alter, $value);
      if ($this->options['alter']['more_link'] && strlen($value) < $length) {
        $tokens = $this->getRenderTokens($alter);
        $more_link_text = $this->options['alter']['more_link_text'] ? $this->options['alter']['more_link_text'] : $this->t('more');
        $more_link_text = strtr(Xss::filterAdmin($more_link_text), $tokens);
        $more_link_path = $this->options['alter']['more_link_path'];
        $more_link_path = strip_tags(Html::decodeEntities($this->viewsTokenReplace($more_link_path, $tokens)));

        // Make sure that paths which were run through URL generation work as
        // well.
        $base_path = base_path();
        // Checks whether the path starts with the base_path.
        if (strpos($more_link_path, $base_path) === 0) {
          $more_link_path = Unicode::substr($more_link_path, Unicode::strlen($base_path));
        }

        // @todo Views should expect and store a leading /. See
        //   https://www.drupal.org/node/2423913.
        $options = array(
          'attributes' => array(
            'class' => array(
              'views-more-link',
            ),
          ),
        );
        if (UrlHelper::isExternal($more_link_path)) {
          $more_link_url = CoreUrl::fromUri($more_link_path, $options);
        }
        else {
          $more_link_url = CoreUrl::fromUserInput('/' . $more_link_path, $options);
        }
        $more_link = ' ' . $this->linkGenerator()->generate($more_link_text, $more_link_url);
      }
    }

    if (!empty($alter['nl2br'])) {
      $value = nl2br($value);
    }

    if ($value_is_safe) {
      $value = ViewsRenderPipelineMarkup::create($value);
    }
    $this->last_render_text = $value;

    if (!empty($alter['make_link']) && (!empty($alter['path']) || !empty($alter['url']))) {
      if (!isset($tokens)) {
        $tokens = $this->getRenderTokens($alter);
      }
      $value = $this->renderAsLink($alter, $value, $tokens);
    }

    // Preserve whether or not the string is safe. Since $more_link comes from
    // \Drupal::l(), it is safe to append. Check if the value is an instance of
    // \Drupal\Component\Render\MarkupInterface here because renderAsLink()
    // can return both safe and unsafe values.
    if ($value instanceof MarkupInterface) {
      return ViewsRenderPipelineMarkup::create($value . $more_link);
    }
    else {
      // If the string is not already marked safe, it is still OK to return it
      // because it will be sanitized by Twig.
      return $value . $more_link;
    }
  }
 /**
  * {@inheritdoc}
  */
 protected function isLocal($url)
 {
     return !UrlHelper::isExternal($url) || UrlHelper::externalIsLocal($url, $this->getRequestContext()->getCompleteBaseUrl());
 }
Example #17
0
 /**
  * Returns the Url object matching a path.
  *
  * @param string $path
  *   A path (e.g. 'node/1', 'http://drupal.org').
  *
  * @return static
  *   An Url object.
  *
  * @throws \Drupal\Core\Routing\MatchingRouteNotFoundException
  *   Thrown when the path cannot be matched.
  */
 public static function createFromPath($path)
 {
     if (UrlHelper::isExternal($path)) {
         $url = new static($path);
         $url->setExternal();
         return $url;
     }
     // Special case the front page route.
     if ($path == '<front>') {
         $route_name = $path;
         $route_parameters = array();
     } else {
         // Look up the route name and parameters used for the given path.
         try {
             $result = \Drupal::service('router')->match('/' . $path);
         } catch (ResourceNotFoundException $e) {
             throw new MatchingRouteNotFoundException(sprintf('No matching route could be found for the path "%s"', $path), 0, $e);
         }
         $route_name = $result[RouteObjectInterface::ROUTE_NAME];
         $route_parameters = $result['_raw_variables']->all();
     }
     return new static($route_name, $route_parameters);
 }
Example #18
0
 /**
  * {@inheritdoc}
  */
 public function preSave(EntityStorageInterface $storage)
 {
     parent::preSave($storage);
     // This is the easiest way to handle the unique internal path '<front>',
     // since a path marked as external does not need to match a route.
     $this->external = UrlHelper::isExternal($this->link_path) || $this->link_path == '<front>' ? 1 : 0;
     // Try to find a parent link. If found, assign it and derive its menu.
     $parent = $this->findParent($storage);
     if ($parent) {
         $this->plid = $parent->id();
         $this->menu_name = $parent->menu_name;
     } else {
         $this->plid = 0;
     }
     // Directly fill parents for top-level links.
     if ($this->plid == 0) {
         $this->p1 = $this->id();
         for ($i = 2; $i <= MENU_MAX_DEPTH; $i++) {
             $parent_property = "p{$i}";
             $this->{$parent_property} = 0;
         }
         $this->depth = 1;
     } else {
         if ($this->has_children && $this->original) {
             $limit = MENU_MAX_DEPTH - $storage->findChildrenRelativeDepth($this->original) - 1;
         } else {
             $limit = MENU_MAX_DEPTH - 1;
         }
         if ($parent->depth > $limit) {
             return FALSE;
         }
         $this->depth = $parent->depth + 1;
         $this->setParents($parent);
     }
     // Need to check both plid and menu_name, since plid can be 0 in any menu.
     if (isset($this->original) && ($this->plid != $this->original->plid || $this->menu_name != $this->original->menu_name)) {
         $storage->moveChildren($this);
     }
     // Find the route_name.
     if (!$this->external && !isset($this->route_name)) {
         $url = Url::createFromPath($this->link_path);
         $this->route_name = $url->getRouteName();
         $this->route_parameters = $url->getRouteParameters();
     } elseif (empty($this->link_path)) {
         $this->link_path = \Drupal::urlGenerator()->getPathFromRoute($this->route_name, $this->route_parameters);
     }
 }
Example #19
0
 /**
  * Encapsulate UrlHelper::isExternal.
  *
  * @param string $url
  *   The url to evaluate.
  *
  * @return bool
  *   Whether or not the url points to an external location.
  *
  * @codeCoverageIgnore
  */
 protected function isExternal($url)
 {
     return UrlHelper::isExternal($url);
 }
Example #20
0
 /**
  * {@inheritdoc}
  */
 public function doBuildForm($form_id, &$element, &$form_state)
 {
     // Initialize as unprocessed.
     $element['#processed'] = FALSE;
     // Use element defaults.
     if (isset($element['#type']) && empty($element['#defaults_loaded']) && ($info = $this->getElementInfo($element['#type']))) {
         // Overlay $info onto $element, retaining preexisting keys in $element.
         $element += $info;
         $element['#defaults_loaded'] = TRUE;
     }
     // Assign basic defaults common for all form elements.
     $element += array('#required' => FALSE, '#attributes' => array(), '#title_display' => 'before', '#errors' => NULL);
     // Special handling if we're on the top level form element.
     if (isset($element['#type']) && $element['#type'] == 'form') {
         if (!empty($element['#https']) && Settings::get('mixed_mode_sessions', FALSE) && !UrlHelper::isExternal($element['#action'])) {
             global $base_root;
             // Not an external URL so ensure that it is secure.
             $element['#action'] = str_replace('http://', 'https://', $base_root) . $element['#action'];
         }
         // Store a reference to the complete form in $form_state prior to building
         // the form. This allows advanced #process and #after_build callbacks to
         // perform changes elsewhere in the form.
         $form_state['complete_form'] =& $element;
         // Set a flag if we have a correct form submission. This is always TRUE
         // for programmed forms coming from self::submitForm(), or if the form_id
         // coming from the POST data is set and matches the current form_id.
         if ($form_state['programmed'] || !empty($form_state['input']) && (isset($form_state['input']['form_id']) && $form_state['input']['form_id'] == $form_id)) {
             $form_state['process_input'] = TRUE;
         } else {
             $form_state['process_input'] = FALSE;
         }
         // All form elements should have an #array_parents property.
         $element['#array_parents'] = array();
     }
     if (!isset($element['#id'])) {
         $element['#id'] = $this->drupalHtmlId('edit-' . implode('-', $element['#parents']));
     }
     // Add the aria-describedby attribute to associate the form control with its
     // description.
     if (!empty($element['#description'])) {
         $element['#attributes']['aria-describedby'] = $element['#id'] . '--description';
     }
     // Handle input elements.
     if (!empty($element['#input'])) {
         $this->handleInputElement($form_id, $element, $form_state);
     }
     // Allow for elements to expand to multiple elements, e.g., radios,
     // checkboxes and files.
     if (isset($element['#process']) && !$element['#processed']) {
         foreach ($element['#process'] as $process) {
             $element = call_user_func_array($process, array(&$element, &$form_state, &$form_state['complete_form']));
         }
         $element['#processed'] = TRUE;
     }
     // We start off assuming all form elements are in the correct order.
     $element['#sorted'] = TRUE;
     // Recurse through all child elements.
     $count = 0;
     foreach (Element::children($element) as $key) {
         // Prior to checking properties of child elements, their default
         // properties need to be loaded.
         if (isset($element[$key]['#type']) && empty($element[$key]['#defaults_loaded']) && ($info = $this->getElementInfo($element[$key]['#type']))) {
             $element[$key] += $info;
             $element[$key]['#defaults_loaded'] = TRUE;
         }
         // Don't squash an existing tree value.
         if (!isset($element[$key]['#tree'])) {
             $element[$key]['#tree'] = $element['#tree'];
         }
         // Deny access to child elements if parent is denied.
         if (isset($element['#access']) && !$element['#access']) {
             $element[$key]['#access'] = FALSE;
         }
         // Make child elements inherit their parent's #disabled and #allow_focus
         // values unless they specify their own.
         foreach (array('#disabled', '#allow_focus') as $property) {
             if (isset($element[$property]) && !isset($element[$key][$property])) {
                 $element[$key][$property] = $element[$property];
             }
         }
         // Don't squash existing parents value.
         if (!isset($element[$key]['#parents'])) {
             // Check to see if a tree of child elements is present. If so,
             // continue down the tree if required.
             $element[$key]['#parents'] = $element[$key]['#tree'] && $element['#tree'] ? array_merge($element['#parents'], array($key)) : array($key);
         }
         // Ensure #array_parents follows the actual form structure.
         $array_parents = $element['#array_parents'];
         $array_parents[] = $key;
         $element[$key]['#array_parents'] = $array_parents;
         // Assign a decimal placeholder weight to preserve original array order.
         if (!isset($element[$key]['#weight'])) {
             $element[$key]['#weight'] = $count / 1000;
         } else {
             // If one of the child elements has a weight then we will need to sort
             // later.
             unset($element['#sorted']);
         }
         $element[$key] = $this->doBuildForm($form_id, $element[$key], $form_state);
         $count++;
     }
     // The #after_build flag allows any piece of a form to be altered
     // after normal input parsing has been completed.
     if (isset($element['#after_build']) && !isset($element['#after_build_done'])) {
         foreach ($element['#after_build'] as $callable) {
             $element = call_user_func_array($callable, array($element, &$form_state));
         }
         $element['#after_build_done'] = TRUE;
     }
     // If there is a file element, we need to flip a flag so later the
     // form encoding can be set.
     if (isset($element['#type']) && $element['#type'] == 'file') {
         $form_state['has_file_element'] = TRUE;
     }
     // Final tasks for the form element after self::doBuildForm() has run for
     // all other elements.
     if (isset($element['#type']) && $element['#type'] == 'form') {
         // If there is a file element, we set the form encoding.
         if (isset($form_state['has_file_element'])) {
             $element['#attributes']['enctype'] = 'multipart/form-data';
         }
         // If a form contains a single textfield, and the ENTER key is pressed
         // within it, Internet Explorer submits the form with no POST data
         // identifying any submit button. Other browsers submit POST data as
         // though the user clicked the first button. Therefore, to be as
         // consistent as we can be across browsers, if no 'triggering_element' has
         // been identified yet, default it to the first button.
         if (!$form_state['programmed'] && !isset($form_state['triggering_element']) && !empty($form_state['buttons'])) {
             $form_state['triggering_element'] = $form_state['buttons'][0];
         }
         // If the triggering element specifies "button-level" validation and
         // submit handlers to run instead of the default form-level ones, then add
         // those to the form state.
         foreach (array('validate', 'submit') as $type) {
             if (isset($form_state['triggering_element']['#' . $type])) {
                 $form_state[$type . '_handlers'] = $form_state['triggering_element']['#' . $type];
             }
         }
         // If the triggering element executes submit handlers, then set the form
         // state key that's needed for those handlers to run.
         if (!empty($form_state['triggering_element']['#executes_submit_callback'])) {
             $form_state['submitted'] = TRUE;
         }
         // Special processing if the triggering element is a button.
         if (!empty($form_state['triggering_element']['#is_button'])) {
             // Because there are several ways in which the triggering element could
             // have been determined (including from input variables set by
             // JavaScript or fallback behavior implemented for IE), and because
             // buttons often have their #name property not derived from their
             // #parents property, we can't assume that input processing that's
             // happened up until here has resulted in
             // $form_state['values'][BUTTON_NAME] being set. But it's common for
             // forms to have several buttons named 'op' and switch on
             // $form_state['values']['op'] during submit handler execution.
             $form_state['values'][$form_state['triggering_element']['#name']] = $form_state['triggering_element']['#value'];
         }
     }
     return $element;
 }
Example #21
0
 /**
  * Tests external versus internal paths.
  *
  * @dataProvider providerTestIsExternal
  * @covers ::isExternal
  *
  * @param string $path
  *   URL or path to test.
  * @param bool $expected
  *   Expected result.
  */
 public function testIsExternal($path, $expected)
 {
     $isExternal = UrlHelper::isExternal($path);
     $this->assertEquals($expected, $isExternal);
 }
Example #22
0
 /**
  * {@inheritdoc}
  */
 public function doBuildForm($form_id, &$element, FormStateInterface &$form_state)
 {
     // Initialize as unprocessed.
     $element['#processed'] = FALSE;
     // Use element defaults.
     if (isset($element['#type']) && empty($element['#defaults_loaded']) && ($info = $this->elementInfo->getInfo($element['#type']))) {
         // Overlay $info onto $element, retaining preexisting keys in $element.
         $element += $info;
         $element['#defaults_loaded'] = TRUE;
     }
     // Assign basic defaults common for all form elements.
     $element += array('#required' => FALSE, '#attributes' => array(), '#title_display' => 'before', '#description_display' => 'after', '#errors' => NULL);
     // Special handling if we're on the top level form element.
     if (isset($element['#type']) && $element['#type'] == 'form') {
         if (!empty($element['#https']) && !UrlHelper::isExternal($element['#action'])) {
             global $base_root;
             // Not an external URL so ensure that it is secure.
             $element['#action'] = str_replace('http://', 'https://', $base_root) . $element['#action'];
         }
         // Store a reference to the complete form in $form_state prior to building
         // the form. This allows advanced #process and #after_build callbacks to
         // perform changes elsewhere in the form.
         $form_state->setCompleteForm($element);
         // Set a flag if we have a correct form submission. This is always TRUE
         // for programmed forms coming from self::submitForm(), or if the form_id
         // coming from the POST data is set and matches the current form_id.
         $input = $form_state->getUserInput();
         if ($form_state->isProgrammed() || !empty($input) && (isset($input['form_id']) && $input['form_id'] == $form_id)) {
             $form_state->setProcessInput();
             if (isset($element['#token'])) {
                 $input = $form_state->getUserInput();
                 if (empty($input['form_token']) || !$this->csrfToken->validate($input['form_token'], $element['#token'])) {
                     // Set an early form error to block certain input processing since
                     // that opens the door for CSRF vulnerabilities.
                     $this->setInvalidTokenError($form_state);
                     // This value is checked in self::handleInputElement().
                     $form_state->setInvalidToken(TRUE);
                     // Make sure file uploads do not get processed.
                     $this->requestStack->getCurrentRequest()->files = new FileBag();
                 }
             }
         } else {
             $form_state->setProcessInput(FALSE);
         }
         // All form elements should have an #array_parents property.
         $element['#array_parents'] = array();
     }
     if (!isset($element['#id'])) {
         $unprocessed_id = 'edit-' . implode('-', $element['#parents']);
         $element['#id'] = Html::getUniqueId($unprocessed_id);
         // Provide a selector usable by JavaScript. As the ID is unique, its not
         // possible to rely on it in JavaScript.
         $element['#attributes']['data-drupal-selector'] = Html::getId($unprocessed_id);
     } else {
         // Provide a selector usable by JavaScript. As the ID is unique, its not
         // possible to rely on it in JavaScript.
         $element['#attributes']['data-drupal-selector'] = Html::getId($element['#id']);
     }
     // Add the aria-describedby attribute to associate the form control with its
     // description.
     if (!empty($element['#description'])) {
         $element['#attributes']['aria-describedby'] = $element['#id'] . '--description';
     }
     // Handle input elements.
     if (!empty($element['#input'])) {
         $this->handleInputElement($form_id, $element, $form_state);
     }
     // Allow for elements to expand to multiple elements, e.g., radios,
     // checkboxes and files.
     if (isset($element['#process']) && !$element['#processed']) {
         foreach ($element['#process'] as $callback) {
             $complete_form =& $form_state->getCompleteForm();
             $element = call_user_func_array($form_state->prepareCallback($callback), array(&$element, &$form_state, &$complete_form));
         }
         $element['#processed'] = TRUE;
     }
     // We start off assuming all form elements are in the correct order.
     $element['#sorted'] = TRUE;
     // Recurse through all child elements.
     $count = 0;
     if (isset($element['#access'])) {
         $access = $element['#access'];
         $inherited_access = NULL;
         if ($access instanceof AccessResultInterface && !$access->isAllowed() || $access === FALSE) {
             $inherited_access = $access;
         }
     }
     foreach (Element::children($element) as $key) {
         // Prior to checking properties of child elements, their default
         // properties need to be loaded.
         if (isset($element[$key]['#type']) && empty($element[$key]['#defaults_loaded']) && ($info = $this->elementInfo->getInfo($element[$key]['#type']))) {
             $element[$key] += $info;
             $element[$key]['#defaults_loaded'] = TRUE;
         }
         // Don't squash an existing tree value.
         if (!isset($element[$key]['#tree'])) {
             $element[$key]['#tree'] = $element['#tree'];
         }
         // Children inherit #access from parent.
         if (isset($inherited_access)) {
             $element[$key]['#access'] = $inherited_access;
         }
         // Make child elements inherit their parent's #disabled and #allow_focus
         // values unless they specify their own.
         foreach (array('#disabled', '#allow_focus') as $property) {
             if (isset($element[$property]) && !isset($element[$key][$property])) {
                 $element[$key][$property] = $element[$property];
             }
         }
         // Don't squash existing parents value.
         if (!isset($element[$key]['#parents'])) {
             // Check to see if a tree of child elements is present. If so,
             // continue down the tree if required.
             $element[$key]['#parents'] = $element[$key]['#tree'] && $element['#tree'] ? array_merge($element['#parents'], array($key)) : array($key);
         }
         // Ensure #array_parents follows the actual form structure.
         $array_parents = $element['#array_parents'];
         $array_parents[] = $key;
         $element[$key]['#array_parents'] = $array_parents;
         // Assign a decimal placeholder weight to preserve original array order.
         if (!isset($element[$key]['#weight'])) {
             $element[$key]['#weight'] = $count / 1000;
         } else {
             // If one of the child elements has a weight then we will need to sort
             // later.
             unset($element['#sorted']);
         }
         $element[$key] = $this->doBuildForm($form_id, $element[$key], $form_state);
         $count++;
     }
     // The #after_build flag allows any piece of a form to be altered
     // after normal input parsing has been completed.
     if (isset($element['#after_build']) && !isset($element['#after_build_done'])) {
         foreach ($element['#after_build'] as $callback) {
             $element = call_user_func_array($form_state->prepareCallback($callback), array($element, &$form_state));
         }
         $element['#after_build_done'] = TRUE;
     }
     // If there is a file element, we need to flip a flag so later the
     // form encoding can be set.
     if (isset($element['#type']) && $element['#type'] == 'file') {
         $form_state->setHasFileElement();
     }
     // Final tasks for the form element after self::doBuildForm() has run for
     // all other elements.
     if (isset($element['#type']) && $element['#type'] == 'form') {
         // If there is a file element, we set the form encoding.
         if ($form_state->hasFileElement()) {
             $element['#attributes']['enctype'] = 'multipart/form-data';
         }
         // Allow Ajax submissions to the form action to bypass verification. This
         // is especially useful for multipart forms, which cannot be verified via
         // a response header.
         $element['#attached']['drupalSettings']['ajaxTrustedUrl'][$element['#action']] = TRUE;
         // If a form contains a single textfield, and the ENTER key is pressed
         // within it, Internet Explorer submits the form with no POST data
         // identifying any submit button. Other browsers submit POST data as
         // though the user clicked the first button. Therefore, to be as
         // consistent as we can be across browsers, if no 'triggering_element' has
         // been identified yet, default it to the first button.
         $buttons = $form_state->getButtons();
         if (!$form_state->isProgrammed() && !$form_state->getTriggeringElement() && !empty($buttons)) {
             $form_state->setTriggeringElement($buttons[0]);
         }
         $triggering_element = $form_state->getTriggeringElement();
         // If the triggering element specifies "button-level" validation and
         // submit handlers to run instead of the default form-level ones, then add
         // those to the form state.
         if (isset($triggering_element['#validate'])) {
             $form_state->setValidateHandlers($triggering_element['#validate']);
         }
         if (isset($triggering_element['#submit'])) {
             $form_state->setSubmitHandlers($triggering_element['#submit']);
         }
         // If the triggering element executes submit handlers, then set the form
         // state key that's needed for those handlers to run.
         if (!empty($triggering_element['#executes_submit_callback'])) {
             $form_state->setSubmitted();
         }
         // Special processing if the triggering element is a button.
         if (!empty($triggering_element['#is_button'])) {
             // Because there are several ways in which the triggering element could
             // have been determined (including from input variables set by
             // JavaScript or fallback behavior implemented for IE), and because
             // buttons often have their #name property not derived from their
             // #parents property, we can't assume that input processing that's
             // happened up until here has resulted in
             // $form_state->getValue(BUTTON_NAME) being set. But it's common for
             // forms to have several buttons named 'op' and switch on
             // $form_state->getValue('op') during submit handler execution.
             $form_state->setValue($triggering_element['#name'], $triggering_element['#value']);
         }
     }
     return $element;
 }
Example #23
0
 /**
  * Creates a new Url object for 'internal:' URIs.
  *
  * Important note: the URI minus the scheme can NOT simply be validated by a
  * \Drupal\Core\Path\PathValidatorInterface implementation. The semantics of
  * the 'internal:' URI scheme are different:
  * - PathValidatorInterface accepts paths without a leading slash (e.g.
  *   'node/add') as well as 2 special paths: '<front>' and '<none>', which are
  *   mapped to the correspondingly named routes.
  * - 'internal:' URIs store paths with a leading slash that represents the
  *   root — i.e. the front page — (e.g. 'internal:/node/add'), and doesn't
  *   have any exceptions.
  *
  * To clarify, a few examples of path plus corresponding 'internal:' URI:
  * - 'node/add' -> 'internal:/node/add'
  * - 'node/add?foo=bar' -> 'internal:/node/add?foo=bar'
  * - 'node/add#kitten' -> 'internal:/node/add#kitten'
  * - '<front>' -> 'internal:/'
  * - '<front>foo=bar' -> 'internal:/?foo=bar'
  * - '<front>#kitten' -> 'internal:/#kitten'
  * - '<none>' -> 'internal:'
  * - '<none>foo=bar' -> 'internal:?foo=bar'
  * - '<none>#kitten' -> 'internal:#kitten'
  *
  * Therefore, when using a PathValidatorInterface to validate 'internal:'
  * URIs, we must map:
  * - 'internal:' (path component is '')  to the special '<none>' path
  * - 'internal:/' (path component is '/') to the special '<front>' path
  * - 'internal:/some-path' (path component is '/some-path') to 'some-path'
  *
  * @param array $uri_parts
  *   Parts from an URI of the form internal:{path} as from parse_url().
  * @param array $options
  *   An array of options, see static::fromUri() for details.
  *
  * @return \Drupal\Core\Url
  *   A new Url object for a 'internal:' URI.
  *
  * @throws \InvalidArgumentException
  *   Thrown when the URI's path component doesn't have a leading slash.
  */
 protected static function fromInternalUri(array $uri_parts, array $options)
 {
     // Both PathValidator::getUrlIfValidWithoutAccessCheck() and 'base:' URIs
     // only accept/contain paths without a leading slash, unlike 'internal:'
     // URIs, for which the leading slash means "relative to Drupal root" and
     // "relative to Symfony app root" (just like in Symfony/Drupal 8 routes).
     if (empty($uri_parts['path'])) {
         $uri_parts['path'] = '<none>';
     } elseif ($uri_parts['path'] === '/') {
         $uri_parts['path'] = '<front>';
     } else {
         if ($uri_parts['path'][0] !== '/') {
             throw new \InvalidArgumentException(SafeMarkup::format('The internal path component "@path" is invalid. Its path component must have a leading slash, e.g. internal:/foo.', ['@path' => $uri_parts['path']]));
         }
         // Remove the leading slash.
         $uri_parts['path'] = substr($uri_parts['path'], 1);
         if (UrlHelper::isExternal($uri_parts['path'])) {
             throw new \InvalidArgumentException(SafeMarkup::format('The internal path component "@path" is external. You are not allowed to specify an external URL together with internal:/.', ['@path' => $uri_parts['path']]));
         }
     }
     $url = \Drupal::pathValidator()->getUrlIfValidWithoutAccessCheck($uri_parts['path']) ?: static::fromUri('base:' . $uri_parts['path'], $options);
     // Allow specifying additional options.
     $url->setOptions($options + $url->getOptions());
     return $url;
 }
Example #24
0
 /**
  * Retrieves a Drupal path or an absolute path.
  *
  * @param string $path
  *   Drupal path or URL to load into Mink controlled browser.
  * @param array $options
  *   (optional) Options to be forwarded to the url generator.
  *
  * @return string
  *   The retrieved HTML string, also available as $this->getRawContent()
  */
 protected function drupalGet($path, array $options = array())
 {
     $options['absolute'] = TRUE;
     // The URL generator service is not necessarily available yet; e.g., in
     // interactive installer tests.
     if ($this->container->has('url_generator')) {
         if (UrlHelper::isExternal($path)) {
             $url = Url::fromUri($path, $options)->toString();
         } else {
             // This is needed for language prefixing.
             $options['path_processing'] = TRUE;
             $url = Url::fromUri('base:/' . $path, $options)->toString();
         }
     } else {
         $url = $this->getAbsoluteUrl($path);
     }
     $session = $this->getSession();
     $this->prepareRequest();
     $session->visit($url);
     $out = $session->getPage()->getContent();
     // Ensure that any changes to variables in the other thread are picked up.
     $this->refreshVariables();
     return $out;
 }
 /**
  * Sanitize the destination parameter to prevent open redirect attacks.
  *
  * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
  *   The Event to process.
  */
 public function sanitizeDestination(GetResponseEvent $event)
 {
     $request = $event->getRequest();
     // Sanitize the destination parameter (which is often used for redirects) to
     // prevent open redirect attacks leading to other domains. Sanitize both
     // $_GET['destination'] and $_REQUEST['destination'] to protect code that
     // relies on either, but do not sanitize $_POST to avoid interfering with
     // unrelated form submissions. The sanitization happens here because
     // url_is_external() requires the variable system to be available.
     $query_info = $request->query;
     $request_info = $request->request;
     if ($query_info->has('destination') || $request_info->has('destination')) {
         // If the destination is an external URL, remove it.
         if ($query_info->has('destination') && UrlHelper::isExternal($query_info->get('destination'))) {
             $query_info->remove('destination');
             $request_info->remove('destination');
         }
         // If there's still something in $_REQUEST['destination'] that didn't come
         // from $_GET, check it too.
         if ($request_info->has('destination') && (!$query_info->has('destination') || $request_info->get('destination') != $query_info->get('destination')) && UrlHelper::isExternal($request_info->get('destination'))) {
             $request_info->remove('destination');
         }
     }
 }
Example #26
0
 /**
  * Builds an a absolute URL from a system path or a URL object.
  *
  * @param string|\Drupal\Core\Url $path
  *   A system path or a URL.
  * @param array $options
  *   Options to be passed to Url::fromUri().
  *
  * @return string
  *   An absolute URL stsring.
  */
 protected function buildUrl($path, array $options = array())
 {
     if ($path instanceof Url) {
         $url_options = $path->getOptions();
         $options = $url_options + $options;
         $path->setOptions($options);
         return $path->setAbsolute()->toString();
     } elseif ($this->container->has('url_generator')) {
         $force_internal = isset($options['external']) && $options['external'] == FALSE;
         if (!$force_internal && UrlHelper::isExternal($path)) {
             return Url::fromUri($path, $options)->toString();
         } else {
             $uri = $path === '<front>' ? 'base:/' : 'base:/' . $path;
             // Path processing is needed for language prefixing.  Skip it when a
             // path that may look like an external URL is being used as internal.
             $options['path_processing'] = !$force_internal;
             return Url::fromUri($uri, $options)->setAbsolute()->toString();
         }
     } else {
         return $this->getAbsoluteUrl($path);
     }
 }
  /**
   * Retrieves a Drupal path or an absolute path.
   *
   * @param string|\Drupal\Core\Url $path
   *   Drupal path or URL to load into Mink controlled browser.
   * @param array $options
   *   (optional) Options to be forwarded to the url generator.
   *
   * @return string
   *   The retrieved HTML string, also available as $this->getRawContent()
   */
  protected function drupalGet($path, array $options = array()) {
    $options['absolute'] = TRUE;

    if ($path instanceof Url) {
      $url_options = $path->getOptions();
      $options = $url_options + $options;
      $path->setOptions($options);
      $url = $path->setAbsolute()->toString();
    }
    // The URL generator service is not necessarily available yet; e.g., in
    // interactive installer tests.
    elseif ($this->container->has('url_generator')) {
      if (UrlHelper::isExternal($path)) {
        $url = Url::fromUri($path, $options)->toString();
      }
      else {
        // This is needed for language prefixing.
        $options['path_processing'] = TRUE;
        $url = Url::fromUri('base:/' . $path, $options)->toString();
      }
    }
    else {
      $url = $this->getAbsoluteUrl($path);
    }
    $session = $this->getSession();

    $this->prepareRequest();
    $session->visit($url);
    $out = $session->getPage()->getContent();

    // Ensure that any changes to variables in the other thread are picked up.
    $this->refreshVariables();

    if ($this->htmlOutputEnabled) {
      $html_output = 'GET request to: ' . $url .
        '<hr />Ending URL: ' . $this->getSession()->getCurrentUrl();
      $html_output .= '<hr />' . $out;
      $html_output .= $this->getHtmlOutputHeaders();
      $this->htmlOutput($html_output);
    }

    return $out;
  }