You are here

public function SamlController::metadata in SAML Authentication 8.3

Same name and namespace in other branches
  1. 8 src/Controller/SamlController.php \Drupal\samlauth\Controller\SamlController::metadata()
  2. 8.2 src/Controller/SamlController.php \Drupal\samlauth\Controller\SamlController::metadata()
  3. 4.x src/Controller/SamlController.php \Drupal\samlauth\Controller\SamlController::metadata()

Displays service provider metadata XML for iDP autoconfiguration.

Return value

\Symfony\Component\HttpFoundation\Response The HTTP response to send back.

1 string reference to 'SamlController::metadata'
samlauth.routing.yml in ./samlauth.routing.yml
samlauth.routing.yml

File

src/Controller/SamlController.php, line 184

Class

SamlController
Returns responses for samlauth module routes.

Namespace

Drupal\samlauth\Controller

Code

public function metadata() {
  $config = $this
    ->config(self::CONFIG_OBJECT_NAME);
  try {

    // Things we need to take into account:
    // - The validUntil and cacheDuration properties are optional in the
    //   SAML spec, but the SAML PHP Toolkit always assigns values. (At the
    //   time of checking: "2 days into the future" and "1 week",
    //   respectively. No reason provided for these figures.)
    // - The only info we can find so far is one hint at wiki.shibboleth.net
    //   MetadataManagementBestPractices: "[cacheDuration] is merely a hint
    //   but metadata expiration [validUntil] is absolute". This matches bug
    //   reports: if we send a validUntil in the past, logins stop working.
    // - We want the HTTP response (with XML contents) to be cacheable
    //   (which, hand-wavy, means 2 things: the Drupal render cache which is
    //   controlled by CacheableResponse, and whatever other HTTP proxies
    //   there may be which are controlled by HTTP headers.) Unlike the
    //   SAML validUntil, we can turn this off for testing.
    // - Once a cacheable response is sent, (we'll assume) we cannot purge it.
    //   Once any response is sent, we cannot purge data from the requester
    //   (IdP).
    // So:
    // - We must make sure no validUntil date in a cached response is ever in
    //   the past - i.e. it must be equal/larger than the response 'expires'
    //   value.
    // - For configuration values, let's make the 'validUntil period'
    //   configurable, plus a checkbox for response caching. Let's set the
    //   response expiry to (validUntil - 10 seconds) to sidestep weird
    //   response delays.
    // - For the cacheDuration value, we don't have much of an idea what is a
    //   good value - except, given the above defaults, it apparently doesn't
    //   matter much if it is a lot higher than validUntil. A cached response
    //   on our side could in extreme circumstances indicate a 'validUntil'
    //   of 10 seconds from now, and a 'cacheDuration' of a week. Is that
    //   bad? Apparently not, if "validUntil is absolute".
    $metadata_valid = $config
      ->get('metadata_valid_secs') ?: Metadata::TIME_VALID;
    $metadata = $this->saml
      ->getMetadata(time() + $metadata_valid);

    // Default is TRUE for existing installs.
    if ($config
      ->get('metadata_cache_http') ?? TRUE) {
      $response = new CacheableResponse($metadata, 200, [
        'Content-Type' => 'text/xml',
      ]);
      $response
        ->setMaxAge($metadata_valid > 10 ? $metadata_valid - 10 : $metadata_valid);
    }
    else {
      $response = new Response($metadata, 200, [
        'Content-Type' => 'text/xml',
      ]);
    }
  } catch (\Exception $e) {

    // This (invoking the exception handling that executes inside a render
    // context) is an awfully convoluted way of handling the exception - but
    // it reuses code and generates the redirect response in a 'protected'
    // way. (Is it even useful to redirect to the front page with an error
    // message? It will not help non-humans requesting the XML document. But
    // humans checking this path will at least see a better hint of what's
    // going on, than if we just return Drupal's plain general exception
    // response. And rendering an error page without redirecting... seems too
    // much effort.)
    $function = function () use ($e) {
      throw $e;
    };
    $response = $this
      ->getTrustedRedirectResponse($function, 'processing SAML SP metadata', '<front>');
  }
  return $response;
}