Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / media / src / Controller / OEmbedIframeController.php
diff --git a/web/core/modules/media/src/Controller/OEmbedIframeController.php b/web/core/modules/media/src/Controller/OEmbedIframeController.php
new file mode 100644 (file)
index 0000000..7a103d4
--- /dev/null
@@ -0,0 +1,182 @@
+<?php
+
+namespace Drupal\media\Controller;
+
+use Drupal\Component\Utility\Crypt;
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Cache\CacheableResponse;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Render\RenderContext;
+use Drupal\Core\Render\RendererInterface;
+use Drupal\Core\Url;
+use Drupal\media\IFrameMarkup;
+use Drupal\media\IFrameUrlHelper;
+use Drupal\media\OEmbed\ResourceException;
+use Drupal\media\OEmbed\ResourceFetcherInterface;
+use Drupal\media\OEmbed\UrlResolverInterface;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+
+/**
+ * Controller which renders an oEmbed resource in a bare page (without blocks).
+ *
+ * This controller is meant to render untrusted third-party HTML returned by
+ * an oEmbed provider in an iframe, so as to mitigate the potential dangers of
+ * of displaying third-party markup (i.e., XSS). The HTML returned by this
+ * controller should not be trusted, and should *never* be displayed outside
+ * of an iframe.
+ *
+ * @internal
+ *   This is an internal part of the oEmbed system and should only be used by
+ *   oEmbed-related code in Drupal core.
+ */
+class OEmbedIframeController implements ContainerInjectionInterface {
+
+  /**
+   * The oEmbed resource fetcher service.
+   *
+   * @var \Drupal\media\OEmbed\ResourceFetcherInterface
+   */
+  protected $resourceFetcher;
+
+  /**
+   * The oEmbed URL resolver service.
+   *
+   * @var \Drupal\media\OEmbed\UrlResolverInterface
+   */
+  protected $urlResolver;
+
+  /**
+   * The renderer service.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * The logger channel.
+   *
+   * @var \Psr\Log\LoggerInterface
+   */
+  protected $logger;
+
+  /**
+   * The iFrame URL helper service.
+   *
+   * @var \Drupal\media\IFrameUrlHelper
+   */
+  protected $iFrameUrlHelper;
+
+  /**
+   * Constructs an OEmbedIframeController instance.
+   *
+   * @param \Drupal\media\OEmbed\ResourceFetcherInterface $resource_fetcher
+   *   The oEmbed resource fetcher service.
+   * @param \Drupal\media\OEmbed\UrlResolverInterface $url_resolver
+   *   The oEmbed URL resolver service.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer service.
+   * @param \Psr\Log\LoggerInterface $logger
+   *   The logger channel.
+   * @param \Drupal\media\IFrameUrlHelper $iframe_url_helper
+   *   The iFrame URL helper service.
+   */
+  public function __construct(ResourceFetcherInterface $resource_fetcher, UrlResolverInterface $url_resolver, RendererInterface $renderer, LoggerInterface $logger, IFrameUrlHelper $iframe_url_helper) {
+    $this->resourceFetcher = $resource_fetcher;
+    $this->urlResolver = $url_resolver;
+    $this->renderer = $renderer;
+    $this->logger = $logger;
+    $this->iFrameUrlHelper = $iframe_url_helper;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('media.oembed.resource_fetcher'),
+      $container->get('media.oembed.url_resolver'),
+      $container->get('renderer'),
+      $container->get('logger.factory')->get('media'),
+      $container->get('media.oembed.iframe_url_helper')
+    );
+  }
+
+  /**
+   * Renders an oEmbed resource.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request object.
+   *
+   * @return \Symfony\Component\HttpFoundation\Response
+   *   The response object.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   *   Will be thrown if the 'hash' parameter does not match the expected hash
+   *   of the 'url' parameter.
+   */
+  public function render(Request $request) {
+    $url = $request->query->get('url');
+    $max_width = $request->query->getInt('max_width', NULL);
+    $max_height = $request->query->getInt('max_height', NULL);
+
+    // Hash the URL and max dimensions, and ensure it is equal to the hash
+    // parameter passed in the query string.
+    $hash = $this->iFrameUrlHelper->getHash($url, $max_width, $max_height);
+    if (!Crypt::hashEquals($hash, $request->query->get('hash', ''))) {
+      throw new AccessDeniedHttpException('This resource is not available');
+    }
+
+    // Return a response instead of a render array so that the frame content
+    // will not have all the blocks and page elements normally rendered by
+    // Drupal.
+    $response = new CacheableResponse();
+    $response->addCacheableDependency(Url::createFromRequest($request));
+
+    try {
+      $resource_url = $this->urlResolver->getResourceUrl($url, $max_width, $max_height);
+      $resource = $this->resourceFetcher->fetchResource($resource_url);
+
+      // Render the content in a new render context so that the cacheability
+      // metadata of the rendered HTML will be captured correctly.
+      $element = [
+        '#theme' => 'media_oembed_iframe',
+        // Even though the resource HTML is untrusted, IFrameMarkup::create()
+        // will create a trusted string. The only reason this is okay is
+        // because we are serving it in an iframe, which will mitigate the
+        // potential dangers of displaying third-party markup.
+        '#media' => IFrameMarkup::create($resource->getHtml()),
+        '#cache' => [
+          // Add the 'rendered' cache tag as this response is not processed by
+          // \Drupal\Core\Render\MainContent\HtmlRenderer::renderResponse().
+          'tags' => ['rendered'],
+        ],
+      ];
+      $content = $this->renderer->executeInRenderContext(new RenderContext(), function () use ($resource, $element) {
+        return $this->renderer->render($element);
+      });
+      $response
+        ->setContent($content)
+        ->addCacheableDependency($resource)
+        ->addCacheableDependency(CacheableMetadata::createFromRenderArray($element));
+    }
+    catch (ResourceException $e) {
+      // Prevent the response from being cached.
+      $response->setMaxAge(0);
+
+      // The oEmbed system makes heavy use of exception wrapping, so log the
+      // entire exception chain to help with troubleshooting.
+      do {
+        // @todo Log additional information from ResourceException, to help with
+        // debugging, in https://www.drupal.org/project/drupal/issues/2972846.
+        $this->logger->error($e->getMessage());
+        $e = $e->getPrevious();
+      } while ($e);
+    }
+
+    return $response;
+  }
+
+}