You are here

protected function ExecuteInRenderContextTrait::getTrustedRedirectResponse in SAML Authentication 4.x

Same name and namespace in other branches
  1. 8.3 src/Controller/ExecuteInRenderContextTrait.php \Drupal\samlauth\Controller\ExecuteInRenderContextTrait::getTrustedRedirectResponse()

Executes code in a render context; generates a TrustedRedirectResponse.

Code which can use a 'regular' RedirectResponse (which isn't cacheable) doesn't need this special protection. (This is why Drupal Core itself doesn't suffer from "leaked metadata" exceptions.) However, any code that wants to redirect to an external URL is forced to use a cacheable response and therefore susceptible to the "leaked metadata" exception. (RedirectResponseSubscriber throws an exception if we try to use a non-cacheable response.)

As noted at executeInRenderContext() comments: in general this code is only suitable for responses whose responses will never be cached. However, since a redirect (generally) doesn't contain much else than a URL, the consequences of caching the response are probably easy enough to predict.

Parameters

callable $callable: A callable that must return a URL to redirect to, in the form of a Url object or a string containing a full absolute URL.

string $while: (Optional) description of when we're doing this, for error logging.

string $redirect_route_on_exception: (Optional) Drupal route name to redirect to and return from this method as a TrustedRedirectResponse, instead of breaking, if an exception is thrown in the callable. This will call handleExceptionInRenderContext() before constructing the redirect URL - which will log and display the message (unless overridden). Suggested value: "<front>".

Return value

\Drupal\Core\Routing\TrustedRedirectResponse A response object.

See also

ExecuteInRenderContextTrait::executeInRenderContext()

4 calls to ExecuteInRenderContextTrait::getTrustedRedirectResponse()
SamlController::acs in src/Controller/SamlController.php
Performs the Attribute Consumer Service.
SamlController::changepw in src/Controller/SamlController.php
Redirects to the 'Change Password' service.
SamlController::getShortenedRedirectResponse in src/Controller/SamlController.php
Gets a redirect response and modifies it a bit.
SamlController::metadata in src/Controller/SamlController.php
Displays service provider metadata XML for iDP autoconfiguration.

File

src/Controller/ExecuteInRenderContextTrait.php, line 175

Class

ExecuteInRenderContextTrait
Helper code for executing a callable inside a render context.

Namespace

Drupal\samlauth\Controller

Code

protected function getTrustedRedirectResponse(callable $callable, $while = '', $redirect_route_on_exception = '') {

  // Since even Url::from*() and Url::toString(TRUE) calls themselves can
  // indirectly call other code that leaks metadata, we need to execute those
  // inside the render context too. So we have to wrap the callable inside
  // another anonymous function - which returns a GeneratedUrl object (unlike
  // $callable which returns either a string or a Url object).
  $function = $redirect_route_on_exception ? function () use ($callable, $while, $redirect_route_on_exception) {
    try {
      $url = $callable();
      if (is_object($url)) {
        if (!$url instanceof Url) {
          throw new \RuntimeException('Object being returned/handled by getTrustedRedirectResponse() callable is not a Url.');
        }

        // We do things 'correctly' ourselves: where a toString() call
        // would bubble up the cacheability metadata in the render context
        // (and cause a warning to be logged), toString() returns it in the
        // GeneratedUrl object and passes it back for the caller to do
        // something appropriate with.
        $url = $url
          ->toString(TRUE);
      }
    } catch (\Exception $e) {

      // If this call throws any kind of exception, that's a fatal error.
      $url = $this
        ->handleExceptionInRenderContext($e, $redirect_route_on_exception, $while);
      if (is_object($url)) {
        if (!$url instanceof Url) {
          throw new \RuntimeException('Object handleExceptionInRenderContext() callable is not a Url.');
        }
        $url = $url
          ->toString(TRUE);
      }
    }
    return $url;
  } : function () use ($callable) {
    $url = $callable();
    if (is_object($url)) {
      if (!$url instanceof Url) {
        throw new \RuntimeException('Object being returned/handled by getTrustedRedirectResponse() callable is not a Url.');
      }
      $url = $url
        ->toString(TRUE);
    }
    return $url;
  };
  $url = $this
    ->executeInRenderContext($function, $while);
  if (is_object($url)) {
    if (!$url instanceof GeneratedUrl) {

      // This can only happen if executeInRenderContext() is wrongly extended.
      throw new \RuntimeException('Object being returned/handled by executeInRenderContext() callable is not a GeneratedUrl.');
    }
    $response = new TrustedRedirectResponse($url
      ->getGeneratedUrl());
    $response
      ->addCacheableDependency($url);
  }
  else {
    $response = new TrustedRedirectResponse($url);
  }
  return $response;
}