Mercurial > hg > isophonics-drupal-site
view core/modules/media/src/Controller/OEmbedIframeController.php @ 18:af1871eacc83
Update to Drupal core 8.7.1
author | Chris Cannam |
---|---|
date | Thu, 09 May 2019 15:33:08 +0100 |
parents | 129ea1e6d783 |
children |
line wrap: on
line source
<?php namespace Drupal\media\Controller; use Drupal\Component\Utility\Crypt; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Render\HtmlResponse; 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 HtmlResponse(); $response->addCacheableDependency(Url::createFromRequest($request)); try { $resource_url = $this->urlResolver->getResourceUrl($url, $max_width, $max_height); $resource = $this->resourceFetcher->fetchResource($resource_url); $placeholder_token = Crypt::randomBytesBase64(55); // 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'], ], '#attached' => [ 'html_response_attachment_placeholders' => [ 'styles' => '<css-placeholder token="' . $placeholder_token . '">', ], 'library' => [ 'media/oembed.frame', ], ], '#placeholder_token' => $placeholder_token, ]; $content = $this->renderer->executeInRenderContext(new RenderContext(), function () use ($resource, $element) { return $this->renderer->render($element); }); $response ->setContent($content) ->setAttachments($element['#attached']) ->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; } }