You are here

public function SamlSPController::consume in SAML Service Provider 4.x

Same name and namespace in other branches
  1. 8.3 src/Controller/SamlSPController.php \Drupal\saml_sp\Controller\SamlSPController::consume()
  2. 8.2 src/Controller/SamlSPController.php \Drupal\saml_sp\Controller\SamlSPController::consume()
  3. 3.x src/Controller/SamlSPController.php \Drupal\saml_sp\Controller\SamlSPController::consume()

Receive data back from the IdP.

1 string reference to 'SamlSPController::consume'
saml_sp.routing.yml in ./saml_sp.routing.yml
saml_sp.routing.yml

File

src/Controller/SamlSPController.php, line 38

Class

SamlSPController
Provides route responses for the SAML SP module.

Namespace

Drupal\saml_sp\Controller

Code

public function consume() {
  if (!$this
    ->validAuthenticationResponse()) {
    return new RedirectResponse(Url::fromRoute('<front>')
      ->toString());
  }

  // The \OneLogin\Saml2\Response object uses the settings to verify the
  // validity of a request, in \OneLogin\Saml2\Response::isValid(), via
  // XMLSecurityDSig. Extract the incoming ID (the `inresponseto` parameter
  // of the `<samlp:response` XML node).
  if ($inbound_id = _saml_sp__extract_inbound_id($_POST['SAMLResponse'])) {
    if ($request = saml_sp__get_tracked_request($inbound_id)) {
      $idp = saml_sp_idp_load($request['idp']);

      // Try to check the validity of the samlResponse.
      try {
        $certs = $idp
          ->getX509Cert();
        if (!is_array($certs)) {
          $certs = [
            $certs,
          ];
        }
        $is_valid = FALSE;

        // Go through each cert and see if any provides a valid response.
        foreach ($certs as $cert) {
          if ($is_valid) {
            continue;
          }
          $idp
            ->setX509Cert([
            $cert,
          ]);
          $settings = saml_sp__get_settings($idp);

          // Creating Saml2 Settings object from array:
          $saml_settings = new Settings($settings);
          $saml_response = new Saml2_Response($saml_settings, $_POST['SAMLResponse']);

          // $saml_response->isValid() will throw various exceptions
          // to communicate any errors. Sadly, these are all of type
          // Exception - no subclassing.
          $is_valid = $saml_response
            ->isValid();
        }
      } catch (Exception $e) {

        // @TODO: Inspect the Exceptions, and log a meaningful error condition.
        \Drupal::logger('saml_sp')
          ->error('Invalid response, %exception', [
          '%exception' => $e->message,
        ]);
        $is_valid = FALSE;
      }

      // Remove the now-expired tracked request.
      $store = saml_sp_get_tempstore('track_request');
      $store
        ->delete($inbound_id);
      if (!$is_valid) {
        $error = $saml_response
          ->getError();
        list($problem) = array_reverse(explode(' ', $error));
        switch ($problem) {
          case 'Responder':
            $message = t('There was a problem with the response from @idp_name. Please try again later.', [
              '@idp_name' => $idp
                ->label(),
            ]);
            break;
          case 'Requester':
            $message = t('There was an issue with the request made to @idp_name. Please try again later.', [
              '@idp_name' => $idp
                ->label(),
            ]);
            break;
          case 'VersionMismatch':
            $message = t('SAML VersionMismatch between @idp_name and @site_name. Please try again later.', [
              '@idp_name' => $idp
                ->label(),
              '@site_name' => variable_get('site_name', 'Drupal'),
            ]);
            break;
        }
        if (!empty($message)) {
          \Drupal::messenger()
            ->addMessage($message, MessengerInterface::TYPE_ERROR);
        }
        \Drupal::logger('saml_sp')
          ->error('Invalid response, @error: <pre>@response</pre>', [
          '@error' => $error,
          '@response' => print_r($saml_response->response, TRUE),
        ]);
      }

      // Invoke the callback function.
      $callback = $request['callback'];
      $result = $callback($is_valid, $saml_response, $idp);

      // The callback *should* redirect the user to a valid page.
      // Provide a fail-safe just in case it doesn't.
      if (empty($result)) {
        return new RedirectResponse(Url::fromRoute('user.page')
          ->toString());
      }
      else {
        return $result;
      }
    }
    else {
      \Drupal::logger('saml_sp')
        ->error('Request with inbound ID @id not found.', [
        '@id' => $inbound_id,
      ]);
    }
  }

  // Failover: redirect to the homepage.
  \Drupal::logger('saml_sp')
    ->warning('Failover: redirect to the homepage. No inbound ID or something.');
  return new RedirectResponse(Url::fromRoute('<front>')
    ->toString());
}