Mercurial > hg > cmmr2012-drupal-site
comparison core/modules/media/src/Controller/OEmbedIframeController.php @ 4:a9cd425dd02b
Update, including to Drupal core 8.6.10
author | Chris Cannam |
---|---|
date | Thu, 28 Feb 2019 13:11:55 +0000 |
parents | |
children | 12f9dff5fda9 |
comparison
equal
deleted
inserted
replaced
3:307d7a7fd348 | 4:a9cd425dd02b |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\media\Controller; | |
4 | |
5 use Drupal\Component\Utility\Crypt; | |
6 use Drupal\Core\Cache\CacheableMetadata; | |
7 use Drupal\Core\Cache\CacheableResponse; | |
8 use Drupal\Core\DependencyInjection\ContainerInjectionInterface; | |
9 use Drupal\Core\Render\RenderContext; | |
10 use Drupal\Core\Render\RendererInterface; | |
11 use Drupal\Core\Url; | |
12 use Drupal\media\IFrameMarkup; | |
13 use Drupal\media\IFrameUrlHelper; | |
14 use Drupal\media\OEmbed\ResourceException; | |
15 use Drupal\media\OEmbed\ResourceFetcherInterface; | |
16 use Drupal\media\OEmbed\UrlResolverInterface; | |
17 use Psr\Log\LoggerInterface; | |
18 use Symfony\Component\DependencyInjection\ContainerInterface; | |
19 use Symfony\Component\HttpFoundation\Request; | |
20 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | |
21 | |
22 /** | |
23 * Controller which renders an oEmbed resource in a bare page (without blocks). | |
24 * | |
25 * This controller is meant to render untrusted third-party HTML returned by | |
26 * an oEmbed provider in an iframe, so as to mitigate the potential dangers of | |
27 * of displaying third-party markup (i.e., XSS). The HTML returned by this | |
28 * controller should not be trusted, and should *never* be displayed outside | |
29 * of an iframe. | |
30 * | |
31 * @internal | |
32 * This is an internal part of the oEmbed system and should only be used by | |
33 * oEmbed-related code in Drupal core. | |
34 */ | |
35 class OEmbedIframeController implements ContainerInjectionInterface { | |
36 | |
37 /** | |
38 * The oEmbed resource fetcher service. | |
39 * | |
40 * @var \Drupal\media\OEmbed\ResourceFetcherInterface | |
41 */ | |
42 protected $resourceFetcher; | |
43 | |
44 /** | |
45 * The oEmbed URL resolver service. | |
46 * | |
47 * @var \Drupal\media\OEmbed\UrlResolverInterface | |
48 */ | |
49 protected $urlResolver; | |
50 | |
51 /** | |
52 * The renderer service. | |
53 * | |
54 * @var \Drupal\Core\Render\RendererInterface | |
55 */ | |
56 protected $renderer; | |
57 | |
58 /** | |
59 * The logger channel. | |
60 * | |
61 * @var \Psr\Log\LoggerInterface | |
62 */ | |
63 protected $logger; | |
64 | |
65 /** | |
66 * The iFrame URL helper service. | |
67 * | |
68 * @var \Drupal\media\IFrameUrlHelper | |
69 */ | |
70 protected $iFrameUrlHelper; | |
71 | |
72 /** | |
73 * Constructs an OEmbedIframeController instance. | |
74 * | |
75 * @param \Drupal\media\OEmbed\ResourceFetcherInterface $resource_fetcher | |
76 * The oEmbed resource fetcher service. | |
77 * @param \Drupal\media\OEmbed\UrlResolverInterface $url_resolver | |
78 * The oEmbed URL resolver service. | |
79 * @param \Drupal\Core\Render\RendererInterface $renderer | |
80 * The renderer service. | |
81 * @param \Psr\Log\LoggerInterface $logger | |
82 * The logger channel. | |
83 * @param \Drupal\media\IFrameUrlHelper $iframe_url_helper | |
84 * The iFrame URL helper service. | |
85 */ | |
86 public function __construct(ResourceFetcherInterface $resource_fetcher, UrlResolverInterface $url_resolver, RendererInterface $renderer, LoggerInterface $logger, IFrameUrlHelper $iframe_url_helper) { | |
87 $this->resourceFetcher = $resource_fetcher; | |
88 $this->urlResolver = $url_resolver; | |
89 $this->renderer = $renderer; | |
90 $this->logger = $logger; | |
91 $this->iFrameUrlHelper = $iframe_url_helper; | |
92 } | |
93 | |
94 /** | |
95 * {@inheritdoc} | |
96 */ | |
97 public static function create(ContainerInterface $container) { | |
98 return new static( | |
99 $container->get('media.oembed.resource_fetcher'), | |
100 $container->get('media.oembed.url_resolver'), | |
101 $container->get('renderer'), | |
102 $container->get('logger.factory')->get('media'), | |
103 $container->get('media.oembed.iframe_url_helper') | |
104 ); | |
105 } | |
106 | |
107 /** | |
108 * Renders an oEmbed resource. | |
109 * | |
110 * @param \Symfony\Component\HttpFoundation\Request $request | |
111 * The request object. | |
112 * | |
113 * @return \Symfony\Component\HttpFoundation\Response | |
114 * The response object. | |
115 * | |
116 * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException | |
117 * Will be thrown if the 'hash' parameter does not match the expected hash | |
118 * of the 'url' parameter. | |
119 */ | |
120 public function render(Request $request) { | |
121 $url = $request->query->get('url'); | |
122 $max_width = $request->query->getInt('max_width', NULL); | |
123 $max_height = $request->query->getInt('max_height', NULL); | |
124 | |
125 // Hash the URL and max dimensions, and ensure it is equal to the hash | |
126 // parameter passed in the query string. | |
127 $hash = $this->iFrameUrlHelper->getHash($url, $max_width, $max_height); | |
128 if (!Crypt::hashEquals($hash, $request->query->get('hash', ''))) { | |
129 throw new AccessDeniedHttpException('This resource is not available'); | |
130 } | |
131 | |
132 // Return a response instead of a render array so that the frame content | |
133 // will not have all the blocks and page elements normally rendered by | |
134 // Drupal. | |
135 $response = new CacheableResponse(); | |
136 $response->addCacheableDependency(Url::createFromRequest($request)); | |
137 | |
138 try { | |
139 $resource_url = $this->urlResolver->getResourceUrl($url, $max_width, $max_height); | |
140 $resource = $this->resourceFetcher->fetchResource($resource_url); | |
141 | |
142 // Render the content in a new render context so that the cacheability | |
143 // metadata of the rendered HTML will be captured correctly. | |
144 $element = [ | |
145 '#theme' => 'media_oembed_iframe', | |
146 // Even though the resource HTML is untrusted, IFrameMarkup::create() | |
147 // will create a trusted string. The only reason this is okay is | |
148 // because we are serving it in an iframe, which will mitigate the | |
149 // potential dangers of displaying third-party markup. | |
150 '#media' => IFrameMarkup::create($resource->getHtml()), | |
151 '#cache' => [ | |
152 // Add the 'rendered' cache tag as this response is not processed by | |
153 // \Drupal\Core\Render\MainContent\HtmlRenderer::renderResponse(). | |
154 'tags' => ['rendered'], | |
155 ], | |
156 ]; | |
157 $content = $this->renderer->executeInRenderContext(new RenderContext(), function () use ($resource, $element) { | |
158 return $this->renderer->render($element); | |
159 }); | |
160 $response | |
161 ->setContent($content) | |
162 ->addCacheableDependency($resource) | |
163 ->addCacheableDependency(CacheableMetadata::createFromRenderArray($element)); | |
164 } | |
165 catch (ResourceException $e) { | |
166 // Prevent the response from being cached. | |
167 $response->setMaxAge(0); | |
168 | |
169 // The oEmbed system makes heavy use of exception wrapping, so log the | |
170 // entire exception chain to help with troubleshooting. | |
171 do { | |
172 // @todo Log additional information from ResourceException, to help with | |
173 // debugging, in https://www.drupal.org/project/drupal/issues/2972846. | |
174 $this->logger->error($e->getMessage()); | |
175 $e = $e->getPrevious(); | |
176 } while ($e); | |
177 } | |
178 | |
179 return $response; | |
180 } | |
181 | |
182 } |