microspid-8.x-1.0-beta12/src/Service/SpidPaswManager.php

src/Service/SpidPaswManager.php
<?php

namespace Drupal\microspid\Service;

use \DOMDocument;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Site\Settings;
use Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * Service to interact with the Spid authentication library.
 */
class SpidPaswManager {

  /**
   * A configuration object.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $config;
  
  /**
   * tracking service manager
   * 
   * 
   */
  protected $tracking;

  /**
   * important values to save.
   *
   */
  protected $entityID;
  protected $nameId;
  protected $session_index;
  protected $idp_filename;

  /**
   * Attributes for federated user.
   *
   * @var array
   */
  protected $attributes;

  /**
   *
   * spid authenticated user
   */
  protected $authenticated = FALSE;
  /**
   * {@inheritdoc}
   *
   * @param ConfigFactoryInterface $config_factory
   *   The configuration factory.
   */
  public function __construct(ConfigFactoryInterface $config_factory) {
    $this->config = $config_factory->get('microspid.settings');
    $this->tracking = \Drupal::service('microspid.tracking');
  }

  /**
   * Forwards the user to the IdP for authentication.
   */
  public function externalAuthenticate() {
    $uri = \Drupal::request()->getUri();
	  $options = array();
	
    if ((isset($_REQUEST['infocert_id']) && $_REQUEST['infocert_id'])) {
        $options['saml:idp'] = $_REQUEST['infocert_id'];
    } elseif ((isset($_REQUEST['poste_id']) && $_REQUEST['poste_id'])) {
        $options['saml:idp'] = $_REQUEST['poste_id'];
    } elseif ((isset($_REQUEST['tim_id']) && $_REQUEST['tim_id'])) {
        $options['saml:idp'] = $_REQUEST['tim_id'];
    } elseif ((isset($_REQUEST['sielte_id']) && $_REQUEST['sielte_id'])) {
        $options['saml:idp'] = $_REQUEST['sielte_id'];
    } elseif ((isset($_REQUEST['aruba_id']) && $_REQUEST['aruba_id'])) {
        $options['saml:idp'] = $_REQUEST['aruba_id'];
    } elseif ((isset($_REQUEST['namirial_id']) && $_REQUEST['namirial_id'])) {
        $options['saml:idp'] = $_REQUEST['namirial_id'];
    } elseif ((isset($_REQUEST['register_id']) && $_REQUEST['register_id'])) {
        $options['saml:idp'] = $_REQUEST['register_id'];
    } elseif ((isset($_REQUEST['intesa_id']) && $_REQUEST['intesa_id'])) {
        $options['saml:idp'] = $_REQUEST['intesa_id'];
    } elseif ((isset($_REQUEST['lepida_id']) && $_REQUEST['lepida_id'])) {
        $options['saml:idp'] = $_REQUEST['lepida_id'];
    } elseif ((isset($_REQUEST['test_id']) && $_REQUEST['test_id'])) {
      $options['saml:idp'] = $_REQUEST['test_id'];
    } elseif ((isset($_REQUEST['demo_id']) && $_REQUEST['demo_id'])) {
      $options['saml:idp'] = $_REQUEST['demo_id'];
    } elseif ((isset($_REQUEST['testonline_id']) && $_REQUEST['testonline_id'])) {
      $options['saml:idp'] = $_REQUEST['testonline_id'];
    } elseif ((isset($_REQUEST['agid_id']) && $_REQUEST['agid_id'])) {
      $options['saml:idp'] = $_REQUEST['agid_id'];
    } else {
        drupal_set_message(t('We\'re sorry. There was a problem. The issue has been logged for the administrator.'));
        $response = new RedirectResponse('');
        $response->send();
        exit;
    }
/*
		$authformat = 'https://www.spid.gov.it/%s';
		$authlevel = $this->config->get('authlevel');
        $options['saml:AuthnContextClassRef'] = sprintf($authformat, $authlevel);
        $options['samlp:RequestedAuthnContext'] = array("Comparison" => "minimum");
		$options['ReturnTo'] = $uri;
*/
	
    $this->authnRequest($options);
  }

  /**
   * Check whether user is authenticated by the IdP.
   *
   * @return bool
   *   If the user is authenticated by the IdP.
   */
  public function isAuthenticated() {
    return $this->authenticated; // TODO $this->instance->isAuthenticated();
  }

  /**
   * Gets the unique id of the user from the IdP.
   *
   * @return string
   *   The authname.
   */
  public function getAuthname() {
    $fn = $this->getFiscalNumber();
    if (empty($fn)) {
      return NULL;
    }
    if ($this->config->get('username_fiscalnumber')) {
      return $fn;
    }
    if ($this->config->get('cf') == '') {
       return $fn;
    }
    $account_search = \Drupal::service('entity.manager')->getStorage('user')->loadByProperties([$this->config->get('cf') => $fn]);
    if ($account_search)
      return reset($account_search)->getUsername();
    $firstname = $this->getAttribute('name');
    $lastname = $this->getAttribute('familyName');
    $compname = $this->getAttribute('companyName');
    if (!empty($compname) && (empty($lastname) || trim($lastname)=='')) {
      return $this->removeUnwantedChars(strtolower($compname));
    }
    $newname = $result = $this->removeUnwantedChars(sprintf("%s.%s", strtolower($lastname), strtolower($firstname)));
    if (strlen($newname) < 4) {
      return $fn;
    }
    $i = 0;
    do {
      if ($i > 0) {
        $result = $newname	. '.' . $i;
      }
      $i++;
    } while (\Drupal::service('entity.manager')->getStorage('user')->loadByProperties(['name' => $result])
      || \Drupal::service('entity.manager')->getStorage('user')->loadByProperties(['name' => 'microspid_' . $result])
      );
    return $result;
  }

  /**
   * @param string original string
   * @return string the input string without accents
   */   
  protected function removeUnwantedChars($str)
  {
    $str = \Drupal::service('transliteration')->transliterate($str);
    $a = array(' ');
    $b = array('_');
    return str_replace($a, $b, $str);
  }

  /**
   * Gets the fiscalNumber of the user from the IdP.
   *
   * @return string
   *   The fiscalNumber.
   */
  public function getFiscalNumber() {
    $cf = $this->getAttribute('fiscalNumber');
    if (empty($cf)) {
      return NULL;
    }
    if (strpos($cf, 'TINIT') === 0) {
      return substr($cf, 6);
    }
    return $cf;
  }

  /**
   * Gets the name attribute.
   *
   * @return string
   *   The name attribute.
  public function getDefaultName() {
    return $this->getAttribute($this->config->get('user_name'));
  }
   */

  /**
   * Gets the mail attribute.
   *
   * @return string
   *   The mail attribute.
   */
  public function getDefaultEmail() {
    return $this->getAttribute($this->config->get('mail_attr'));
  }

  /**
   * Gets all SimpleSAML attributes.
   *
   * @return array
   *   Array of SimpleSAML attributes.
   */
  public function getAttributes() {
    /*
    if (!$this->attributes) {
      $this->attributes = $this->instance->getAttributes();
    }
    */
    return $this->attributes;
  }

  /**
   * Get a specific SimpleSAML attribute.
   *
   * @param string $attribute
   *   The name of the attribute.
   *
   * @return mixed|bool
   *   The attribute value or FALSE.
   *
   */
  public function getAttribute($attribute) {
    $attributes = $this->getAttributes();

    if (isset($attributes)) {
      if (!empty($attributes[$attribute])) {
        return $attributes[$attribute];
      }
    }
    return NULL;
  }

  /**
   * Asks all modules if current federated user is allowed to login.
   *
   * @return bool
   *   Returns FALSE if at least one module returns FALSE.
   */
  public function allowUserByAttribute() {
    $attributes = $this->getAttributes();
    foreach (\Drupal::moduleHandler()->getImplementations('microspid_allow_login') as $module) {
      if (\Drupal::moduleHandler()->invoke($module, 'microspid_allow_login', [$attributes]) === FALSE) {
        return FALSE;
      }
    }
    return TRUE;
  }

  /**
   * Checks if microspid is enabled.
   *
   * @return bool
   *   Whether Spid authentication is enabled or not.
   */
  public function isActivated() {
    if ($this->config->get('activate') == 1) {
      return TRUE;
    }
    return FALSE;
  }

  protected function doSingleLogout() {
    if ($this->config->get('authlevel') == 'SpidL1'
    && $this->config->get('single_logout')
    ) {
      return TRUE;
    }
    return FALSE;
  }
  
  /**
   * Log a user out through the Spid instance.
   *
   * @param string $redirect_path
   *   The path to redirect to after logout.
   */
  public function logout($redirect_path = NULL) {
    if (!$redirect_path) {
      $redirect_path = base_path();
    }

    // Log user logout
    if (isset($_SESSION['spiduser'])) {
      \Drupal::logger('microspid')->notice('User %name disconnecting via SPID', array('%name' => $_SESSION['spiduser'] ));
    }
    if (isset($_SESSION['spiduser']) 
      && $_SESSION['spiduser'] == \Drupal::currentUser()->getUsername() 
      && $this->doSingleLogout()) {
      $options = array(
        'IdP' => $_SESSION['IdP'],
        'EntityID' => $_SESSION['EntityID'],
        'NameID' => $_SESSION['NameID'],
        'SessionIndex' => $_SESSION['SessionIndex'],
      );
      session_destroy();
      $this->logoutRequest($options);
    }
  }
  
  public function getValue($varname) {
    if (!property_exists($this, $varname)) {
      return NULL;
    }
    return $this->$varname;
  }
  
  protected function authnRequest ($options) {
    global $base_url;
    $idp_filename = $this->getIdp($options['saml:idp']);
    //die($idp_filename);
    $idp = $this->loadMetadata($idp_filename, FALSE, $options['saml:idp']);
    if (empty($idp)) {
      drupal_set_message(t('metadata not found'), 'warning', TRUE);
      $response = new RedirectResponse($base_url);
      return $response;
    }

    $url = $this->idpLoginUrl($idp, $post);
    if ($url === FALSE) {
      drupal_set_message(t('connection url not found'), 'warning', TRUE);
      $response = new RedirectResponse($base_url);
      return $response;
    }

    $md = $this->loadMetadata('templates/login.xml');
    $dnode = $all = dom_import_simplexml($md);
    $index = $this->config->get('index');
    $dnode->setAttribute('AssertionConsumerServiceIndex', $index);
    $dnode->setAttribute('AttributeConsumingServiceIndex', $index);
    $authn_req_id = '_' . md5(uniqid(mt_rand(), TRUE));
    $dnode->setAttribute('ID', $authn_req_id);
    $dnode->setAttribute('Destination', $idp->attributes()['entityID']);// $url);
    $instant = gmdate("Y-m-d\TH:i:s\Z");
    $dnode->setAttribute('IssueInstant', $instant);
    $saml = $md->children('urn:oasis:names:tc:SAML:2.0:assertion');
    $dnode = dom_import_simplexml($saml);
    $dnode->setAttribute('NameQualifier', $this->config->get('entityid'));
    $saml->Issuer =  $this->config->get('entityid');
    $protocol = $md->children("urn:oasis:names:tc:SAML:2.0:protocol");
    $saml2 = $protocol->RequestedAuthnContext->children("urn:oasis:names:tc:SAML:2.0:assertion");
    $saml2->AuthnContextClassRef = sprintf('https://www.spid.gov.it/%s',$this->config->get('authlevel'));

    // $xml_data = $md->asXML();
    $xml_data = $this->canonicalizeData($all);
    $this->tracking->createTrack($authn_req_id, $instant, $xml_data);
    
    if ($post === TRUE) {
      $this->post($xml_data, $url, md5($authn_req_id));
    }


    $mod_path = drupal_get_path('module', 'microspid');
    $xml_data = urlencode(base64_encode(gzdeflate($xml_data)));
    $rs = "&RelayState=" . urlencode((string) md5($authn_req_id));
    // 256.
    $sa = '&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256';
    $data = "SAMLRequest=$xml_data$rs$sa";
    
    $certsManager = \Drupal::service('microspid.certs.manager');
    openssl_sign($data, $signature, file_get_contents($certsManager->getPrivateKeyPath()), OPENSSL_ALGO_SHA256);
    $si = '&Signature=' . urlencode(base64_encode($signature));


    header('Pragma: no-cache');
    header('Cache-Control: no-cache, must-revalidate');
    header("Location: $url?SAMLRequest=$xml_data$rs$sa$si");
    exit;
    
  }
  
  protected function logoutRequest ($options) {
    global $base_url;
    $idp = $this->loadMetadata($options['IdP'], FALSE, $options['EntityID']);
    if (empty($idp)) {
      drupal_set_message(t('metadata not found'), 'warning', TRUE);
      $response = new RedirectResponse($base_url);
      return $response;
    }

    $url = $this->idpLogoutUrl($idp, $post);
    if ($url === FALSE) {
      drupal_set_message(t('connection url not found'), 'warning', TRUE);
      $response = new RedirectResponse($base_url);
      return $response;
    }

    $req = $this->loadMetadata('templates/logout.xml');
    $dnode = dom_import_simplexml($req);
    $logout_req_id = '_' . md5(uniqid(mt_rand(), TRUE));
    $dnode->setAttribute('ID', $logout_req_id);
    $dnode->setAttribute('Destination', $idp->attributes()['entityID']);// $url);
    $dnode->setAttribute('IssueInstant', gmdate("Y-m-d\TH:i:s\Z"));
    $saml = $req->children('urn:oasis:names:tc:SAML:2.0:assertion');
    $dnode = dom_import_simplexml($saml);
    $dnode->setAttribute('NameQualifier', $this->config->get('entityid'));
    $saml->Issuer =  $this->config->get('entityid');
    $dnode = dom_import_simplexml($saml->NameID);
    $dnode->setAttribute('NameQualifier', $options['EntityID']);
    $saml->NameID = $options['NameID'];
    $protocol = $req->children("urn:oasis:names:tc:SAML:2.0:protocol");
    $protocol->SessionIndex = $options['SessionIndex'];
    $xml_data = $req->asXML();

    if ($post === TRUE) {
      $this->post($xml_data, $url, md5($logout_req_id));
    }

    $mod_path = drupal_get_path('module', 'microspid');
    $xml_data = urlencode(base64_encode(gzdeflate($xml_data)));
    $rs = "&RelayState=" . urlencode((string) md5($logout_req_id));
    $sa = '&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256';
    $data = "SAMLRequest=" . $xml_data . $rs . $sa;
    $certsManager = \Drupal::service('microspid.certs.manager');
    openssl_sign($data, $signature, file_get_contents($certsManager->getPrivateKeyPath()), OPENSSL_ALGO_SHA256);
    $si = '&Signature=' . urlencode(base64_encode($signature));

    header('Pragma: no-cache');
    header('Cache-Control: no-cache, must-revalidate');
    header("Location: $url?$data$si");
    exit;
  }

  public function loadMetadata($filename, $public = FALSE, $entity_id = NULL) {
    $pathname = drupal_get_path('module', 'microspid') . '/metadata/' . $filename;
    if ($public) {
      $pathname = \Drupal::service('file_system')->realpath('public://microspid') . $filename;
    }
    if ($filename != 'spid-entities-idps.xml') {
      return simplexml_load_file($pathname);
    }
    $entities_file = simplexml_load_file($pathname);
    if ($entities_file === FALSE) {
      return NULL;
    }
    $entities = $entities_file->children("urn:oasis:names:tc:SAML:2.0:metadata");
    foreach ($entities->EntityDescriptor as $descriptor) {
      if ($descriptor->attributes()['entityID'] == $entity_id) {
        return $descriptor;
      }
    }
    return NULL;     
  }

  public function acs($resp) {
    global $base_url;
    
    $data = new \SimpleXMLElement($resp);

    // Prelevare inresponseto etc.
    $requestID = $data->attributes()['InResponseTo'];
    $responseID = $data->attributes()['ID'];
    $version = $data->attributes()['Version'];
    $instant = $data->attributes()['IssueInstant'];

    if ($version != '2.0') {
      return t('Wrong version!');
    }
    //echo ("<pre>");print_r($instant);exit;
    if (empty($instant) || empty($instant[0])) {
      return t('IssueInstant void/missing');
    }
    $u_instant = $this->samlToUnixTime($instant);
    if ($u_instant == 0) {
      return t('SAML date incorrect');
    }
     
    // Controllare destination.
    $destination = $data->attributes()['Destination'];
    if ($destination != $base_url . '/microspid_acs') {
      return t('Invalid destination');
    }

    $resp_signature = $data->children("http://www.w3.org/2000/09/xmldsig#");
    if (!property_exists($resp_signature, 'Signature')) {
      drupal_set_message(t('no response signature'), 'warning');
    }

    // Controllare se successo.
    $protocol = $data->children("urn:oasis:names:tc:SAML:2.0:protocol");
    if (!(property_exists($protocol, 'Status'))
      || !(property_exists($protocol->Status, 'StatusCode'))
    ) {
      return t('no status/statuscode');
    }
    if ($protocol->Status->StatusCode->attributes()['Value'] == 'urn:oasis:names:tc:SAML:2.0:status:Success') {
      $this->authenticated = TRUE;
    }
    else {
      return $this->agidError((string) $protocol->Status->StatusMessage);
    }

    // Controllare issuer.
    $assertion = $data->children("urn:oasis:names:tc:SAML:2.0:assertion");
    if (!property_exists($assertion, 'Issuer') || empty($assertion->Issuer)) {
      return(t('Issuer missing'));
    }
    if (!property_exists($assertion, 'Assertion') || empty($assertion->Assertion)) {
      return(t('Assertion missing'));
    }
    $this->entityID = (string) $assertion->Issuer;
    $format = $assertion->Issuer->attributes()['Format'];
    if ($assertion->Assertion->Issuer == NULL) {
      return(t('Assertion Issuer missing'));
    }
    $format2 = @$assertion->Assertion->Issuer->attributes()['Format'];
    if (!property_exists($assertion->Assertion, 'Issuer') || empty(trim((string)($assertion->Assertion->Issuer))) || $assertion->Assertion->Issuer != $this->entityID || /*trim($format) != trim($format2) ||*/ trim($format2) != 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity') {
      return(t('Issuer incorrect'));
    }
    $ass_signature = $assertion->Assertion->children("http://www.w3.org/2000/09/xmldsig#");
    if (!property_exists($ass_signature, 'Signature')) {
      return (t('no assertion signature'));
    }

    $a_id = $assertion->Assertion->attributes()['ID'];
    if (empty(trim($a_id))) {
      return(t('assertion ID problem'));
    }
    $a_v = $assertion->Assertion->attributes()['Version'];
    if ($a_v != '2.0') {
      return(t('assertion version problem'));
    }
    if (!property_exists($assertion->Assertion, 'Subject')) {
      return(t('assertion subject problem'));
    }
    $a_n = (string)$assertion->Assertion->Subject->NameID;
    $a_n = trim($a_n);
    if (empty($a_n)) {
      return(t('assertion Subject/NameID problem'));
    }
    $a_nf = $assertion->Assertion->Subject->NameID->attributes()['Format'];
    if (empty(trim($a_nf)) || trim($a_nf) != 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient') {
      return(t('assertion NameID format problem'));
    }
    $a_nq = $assertion->Assertion->Subject->NameID->attributes()['NameQualifier'];
    if (empty(trim($a_nq))/* || trim($a_nq) != $requestID*/) {
      return(t('assertion NameID NameQualifier problem'));
    }
    $a_sc = (object)$assertion->Assertion->Subject->SubjectConfirmation;
    if (empty($a_sc)) {
      return(t('assertion SubjectConfirmation problem'));
    }
    $a_scm = trim($a_sc->attributes()['Method']);
    if (empty($a_scm) || $a_scm != 'urn:oasis:names:tc:SAML:2.0:cm:bearer') {
      return(t('assertion SubjectConfirmation method problem'));
    }
    $a_scd = (object)$assertion->Assertion->Subject->SubjectConfirmation->SubjectConfirmationData;
    if (empty($a_scd)) {
      return(t('assertion SubjectConfirmationData problem'));
    }
    $a_scdr = $assertion->Assertion->Subject->SubjectConfirmation->SubjectConfirmationData->attributes()['Recipient'];
    if (empty(trim($a_scdr)) || trim($a_scdr) != $destination) {
      return(t('assertion SubjectConfirmationData Recipient problem'));
    }
    $a_scdi = ($assertion->Assertion->Subject->SubjectConfirmation->SubjectConfirmationData->attributes())['InResponseTo'];
    if (empty(trim($a_scdi)) || trim($a_scdi) != $requestID) {
      return(t('assertion SubjectConfirmationData InResponseTo problem'));
    }
    $a_scdn = ($assertion->Assertion->Subject->SubjectConfirmation->SubjectConfirmationData->attributes())['NotOnOrAfter'];
    $a_scdn_t = $this->samlToUnixTime($a_scdn);
    if (empty(trim($a_scdn)) || empty($a_scdn_t) || $a_scdn_t < $u_instant) {
      return(t('assertion SubjectConfirmationData NotOnOrAfter problem'));
    }

    $a_instant = $assertion->Assertion->attributes()['IssueInstant'];
    $au_instant = $this->samlToUnixTime($a_instant);
    $req_instant = $this->tracking->readTrackInstant($requestID);
    if ($req_instant) {
      $requ_instant = $this->samlToUnixTime($req_instant);
    } else {
      $requ_instant = NULL;
    }
    //echo ($au_instant.'-----'.$requ_instant.'---');die((string)($au_instant-$requ_instant));
    if (
      empty($au_instant) ||
      empty($requ_instant) ||
      $au_instant < ($requ_instant - 500) ||
      $au_instant > ($requ_instant + 500)
    ) {
      return(t('assertion instant problem'));
    }

    $tmp = clone($data);
    $tmp->registerXPathNamespace('ds',"http://www.w3.org/2000/09/xmldsig#");
    $sign = $tmp->xpath('//ds:Signature');
    $owner = NULL;
    if (isset($sign[0])) {
      $dom = dom_import_simplexml($sign[0]);
      $owner = $dom->ownerDocument;
      $dom->parentNode->removeChild($dom);
    }
    if (isset($sign[1])) {
      $dom = dom_import_simplexml($sign[1]);
      $owner = $dom->ownerDocument;
      $dom->parentNode->removeChild($dom);
    }
    if ($owner == NULL) {
      return t('no signature');
    }

    $count = $this->tracking->updateTrack($requestID, $responseID, $instant, $a_instant, $owner->saveXML(), $this->entityID);
    if($count < 1) {
      return t('no such request');
    }
    $this->idp_filename = $this->getIdp($this->entityID);
    if (empty($this->idp_filename)) {
      return t("Can't find IdP metadata");
    }
    // Controllare firma.
    $metadata = $this->loadMetadata($this->idp_filename, FALSE, $this->entityID);
    $md = $metadata->children("urn:oasis:names:tc:SAML:2.0:metadata");
    $ds = $md->IDPSSODescriptor->KeyDescriptor->children("http://www.w3.org/2000/09/xmldsig#");
    $cert = $ds->KeyInfo->X509Data->X509Certificate;

    $test = $this->validateSign($resp, $cert);
    if ($test !== TRUE) {
      return($test === FALSE ? t('Invalid data') : $test);
    }

    // Session id.
    $authn = $assertion->Assertion->AuthnStatement;
    if (empty($authn)) {
      return t('AuthnStatement missing');
    }
    if (!property_exists($assertion->Assertion->AuthnStatement, 'AuthnContext')) {
      return t('AuthnContext missing');
    }
    if (!property_exists($assertion->Assertion->AuthnStatement->AuthnContext, 'AuthnContextClassRef')) {
      return t('AuthnContextClassRef  missing');
    }
    $level = $assertion->Assertion->AuthnStatement->AuthnContext->AuthnContextClassRef;
    //$conf_level = $this->config->get('authlevel');
    if (!($level == 'https://www.spid.gov.it/SpidL1' || $level == 'https://www.spid.gov.it/SpidL2' || $level == 'https://www.spid.gov.it/SpidL3')) {
      return t('AuthnContextClassRef  incorrect');
    }
    $this->session_index = $authn->attributes()['SessionIndex'];

    // NameID.
    $this->nameId = (string) $assertion->Assertion->Subject->NameID;

    // Finestra temporale.
    //echo '<pre>'; print_r($assertion->Assertion->Conditions);exit;
    if (property_exists($assertion->Assertion, 'Conditions') && property_exists($assertion->Assertion->Conditions, 'AudienceRestriction')) {
      $before = trim($assertion->Assertion->Conditions->attributes()['NotBefore']);
      $onorafter = trim($assertion->Assertion->Conditions->attributes()['NotOnOrAfter']);
      if (!empty($before) && !empty($onorafter)) {
        $before = $this->samlToUnixTime($before);
        $onorafter = $this->samlToUnixTime($onorafter);
        if ($before == 0 || $onorafter == 0) {
          return t('Conditions element problem');
        }
        $now = time();
        if (($now + 60) < $before || ($now - 60) >= $onorafter) {
          return t('time out of terms');
        }
      } else {
        return t('Conditions element void');
      }
    } else {
      return t('Conditions element missing');
    }

    // AudienceRestriction
    $defval = $this->config->get('entityid');
    if (empty($defval)) $defval = $base_url . '/microspid_metadata';
    if (!property_exists($assertion->Assertion->Conditions->AudienceRestriction, 'Audience') 
      || $assertion->Assertion->Conditions->AudienceRestriction->Audience != $defval
    ) {
      return t('AudienceRestriction element problem');
    }

    // Attributi SPID.
    if (!property_exists($assertion->Assertion, 'AttributeStatement')/* || empty(trim($assertion->Assertion->AttributeStatement))*/) {
      return t('AttributeStatement element missing');
    }
    $attributes = $assertion->Assertion->AttributeStatement;
    $warn = TRUE;
    foreach ($attributes->Attribute as $attribute) {
      $warn = FALSE;
      $this->attributes[(string) $attribute->attributes()['Name']] = (string)$attribute->AttributeValue;
      if ($this->config->get('debug')) {
        \Drupal::logger('microspid')->debug('%name: %attr', array('%name' => (string) $attribute->attributes()['Name'], '%attr' => (string)$attribute->AttributeValue));
      }
    }
    if ($warn) {
      return t('AttributeStatement element problem');
    }
    return TRUE;
  }

  /**
   * @TODO [microspid_get_slo_request description]
   * @method microspid_get_slo_request
   * @return [type]                    [description]
   */
  protected function getSloRequest() {
    $success = FALSE;
    $post = TRUE;
    if (!isset($_POST['SAMLRequest'])) {
      $post = FALSE;
      $request = isset($_REQUEST['SAMLRequest']) ? $_REQUEST['SAMLRequest'] : NULL;
      $relay = isset($_REQUEST['RelayState']) ? $_REQUEST['RelayState'] : NULL;
    }
    else {
      $request = $_POST['SAMLRequest'];
      $relay = isset($_POST['RelayState']) ? $_POST['RelayState'] : NULL;
    }
    // TODO! uncomment if block.
    if (empty($request)) {
      throw new \Exception(t('nothing to do'));
    }
    $request = base64_decode($request);
    if (!$post) {
      $request = gzinflate($request);
      // TODO! FALSE.
      if ($request === FALSE) {
        drupal_set_message(t('error inflating response'), 'warning');
        $response = new RedirectResponse('');
        $response->send();
        exit;
      }
    }
    // TODO! comment next line
    // $request = file_get_contents('/home/drupal/slo/2.xml');.
    $data = new \SimpleXMLElement($request);
    $requestID = $data->attributes()['ID'];
    $assertion = $data->children("urn:oasis:names:tc:SAML:2.0:assertion");
    $entityID = (string) $assertion->Issuer;
    $idp = $this->getIdp($entityID);
    if (empty($idp)) {
        drupal_set_message(t("Can't find IdP metadata"), 'warning');
        $response = new RedirectResponse('');
        $response->send();
        exit;
    }
    $metadata = $this->loadMetadata($idp, FALSE, $entityID);
    $md = $metadata->children("urn:oasis:names:tc:SAML:2.0:metadata");
    $ds = $md->IDPSSODescriptor->KeyDescriptor->children("http://www.w3.org/2000/09/xmldsig#");
    $cert = $ds->KeyInfo->X509Data->X509Certificate;

    if ($post) {
      $test = $this->validateSign($request, $cert);
    }
    else {
      $test = $this->validateSignRedirect($_SERVER['QUERY_STRING'], $cert);
    }
    // Uncomment if block.
    if ($test !== TRUE) {
      drupal_set_message($test === FALSE ? t('Invalid data') : $test, 'warning');
      $response = new RedirectResponse('');
      $response->send();
      exit;
    }

    $destroy = TRUE;
    $protocol = $data->children("urn:oasis:names:tc:SAML:2.0:protocol");
    if (empty($_SESSION['SessionIndex']) || $protocol->SessionIndex != $_SESSION['SessionIndex']) {
      $destroy = FALSE;
    }

    $this->sloResponse($metadata, $requestID, $entityID, $relay, $destroy);
  }

  /**
   * @TODO [microspid_slo_response description]
   * @method microspid_slo_response
   * @param  [type] $metadata
   *   [description].
   * @param  [type] $inrespto
   *   [description].
   * @param  [type] $destination
   *   [description].
   * @param  [type] $relay
   *   [description].
   * @param  [type] $destroy
   *   [description].
   * @return [type]                              [description]
   */
  protected function sloResponse($metadata, $inrespto, $destination, $relay, $destroy) {
    global $base_url;
    $url = $this->idpResponseUrl($metadata, $post);
    if ($url === FALSE) {
      drupal_set_message(t('connection url not found'));
      $response = new RedirectResponse('');
      $response->send();
      exit;
    }
    $md = $this->loadMetadata('templates/response.xml');
    $dnode = dom_import_simplexml($md);
    $resp_req_id = '_' . md5(uniqid(mt_rand(), TRUE));
    $dnode->setAttribute('ID', $resp_req_id);
    $dnode->setAttribute('InResponseTo', $inrespto);
    $dnode->setAttribute('Destination', $url /*$destination*/);
    $instant = gmdate("Y-m-d\TH:i:s\Z");
    $dnode->setAttribute('IssueInstant', $instant);
    $assertion = $md->children("urn:oasis:names:tc:SAML:2.0:assertion");
    $dnode = dom_import_simplexml($assertion);
    $dnode->setAttribute('NameQualifier', $this->config->get('entityid'));
    $assertion->Issuer = $this->config->get('entityid');

    if ($destroy) {
      session_destroy();
    }

    $xml_data = $md->asXML();

    if ($post === TRUE) {
      $this->post($xml_data, $url, $relay, TRUE);
    }

    $mod_path = drupal_get_path('module', 'microspid');
    $xml_data = urlencode(base64_encode(gzdeflate($xml_data)));
    $rs = $relay === NULL ? '' : "&RelayState=" . urlencode((string) $relay);
    $sa = '&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256';
    $data = "SAMLResponse=" . $xml_data . $rs . $sa;
    openssl_sign($data, $signature, file_get_contents($this->getPrivateKeyPath()), OPENSSL_ALGO_SHA256);
    $si = '&Signature=' . urlencode(base64_encode($signature));

    header('Pragma: no-cache');
    header('Cache-Control: no-cache, must-revalidate');
    header("Location: $url?$data$si");
    exit;
  }

  public function logoutResponseControl($resp, $post) {
    $success = FALSE;
    $data = new \SimpleXMLElement($resp);
    $assertion = $data->children("urn:oasis:names:tc:SAML:2.0:assertion");
    $entityID = (string) $assertion->Issuer;
    $idp = $this->getIdp($entityID);
    if (empty($idp)) {
        drupal_set_message(t("Can't find IdP metadata"), 'warning');
        $response = new RedirectResponse('');
        $response->send();
        exit;
    }
    $metadata = $this->loadMetadata($idp, FALSE, $entityID);
    $md = $metadata->children("urn:oasis:names:tc:SAML:2.0:metadata");
    $ds = $md->IDPSSODescriptor->KeyDescriptor->children("http://www.w3.org/2000/09/xmldsig#");
    $cert = $ds->KeyInfo->X509Data->X509Certificate;

    if ($post) {
      $test = $this->validateSign($resp, $cert);
    }
    else {
      $test = $this->validateSignRedirect($_SERVER['QUERY_STRING'], $cert);
    }
    if ($test !== TRUE) {
      drupal_set_message($test === FALSE ? t('Invalid data') : $test, 'warning');
      $response = new RedirectResponse('');
      $response->send();
      exit;
    }
    $protocol = $data->children("urn:oasis:names:tc:SAML:2.0:protocol");
    if ($protocol->Status->StatusCode->attributes()['Value'] == 'urn:oasis:names:tc:SAML:2.0:status:Success') {
      $success = TRUE;
    }
    return $success;
  }
  
  /**
   * @TODO [microspid_generateGUID description]
   * @method microspid_generateGUID
   * @param  string $prefix
   *   [description].
   * @return [type]                         [description]
   */
  public function generateGUID($prefix = 'pfx') {
    $uuid = md5(uniqid(mt_rand(), TRUE));
    $guid = $prefix . substr($uuid, 0, 8) . "-" .
            substr($uuid, 8, 4) . "-" .
            substr($uuid, 12, 4) . "-" .
            substr($uuid, 16, 4) . "-" .
            substr($uuid, 20, 12);
    return $guid;
  }

  /**
   * @TODO [microspid_get_idp description]
   * @method microspid_get_idp
   * @param  [type] $entityID
   *   [description].
   * @return [type]                      [description]
   */
  
  protected function getIdp($entityID) {
    $entityID = trim($entityID);
    $array = [
      'https://loginspid.aruba.it' => 'spid-entities-idps.xml',
      'https://identity.infocert.it' => 'spid-entities-idps.xml',
      'https://spid.intesa.it' => 'spid-entities-idps.xml',
      'https://id.lepida.it/idp/shibboleth' => 'spid-entities-idps.xml',
      'https://idp.namirialtsp.com/idp' => 'spid-entities-idps.xml',
      'https://posteid.poste.it' => 'spid-entities-idps.xml',
      'https://spid.register.it' => 'spid-entities-idps.xml',
      'https://identity.sieltecloud.it' => 'spid-entities-idps.xml',
      'https://login.id.tim.it/affwebservices/public/saml2sso' => 'spid-entities-idps.xml',
      'spid-testenv-identityserver' => 'test.xml',
      'http://localhost:8088' => 'testenv2.xml',
      'https://idp.spid.gov.it' => 'test-online.xml',
      'https://validator.spid.gov.it' => 'agid.xml',
      'http://localhost:8080' => '_agid.xml',
    ];
    
    return isset($array[$entityID])? $array[$entityID] : NULL;
  }
  

  protected function idpLoginUrl($xml, &$post) {
    $metadata = $xml->children("urn:oasis:names:tc:SAML:2.0:metadata");
    $one = $metadata->IDPSSODescriptor->SingleSignOnService[0];
    $two = $metadata->IDPSSODescriptor->SingleSignOnService[1];
    if ($one->attributes()['Binding'] == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect') {
      $post = FALSE;
    }
    if ($one->attributes()['Binding'] == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST') {
      $post = TRUE;
    }
    if ($post !== -1) {
      return $one->attributes()['Location'];
    }
    if ($two->attributes()['Binding'] == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect') {
      $post = FALSE;
    }
    if ($two->attributes()['Binding'] == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST') {
      $post = TRUE;
    }
    if ($post !== -1) {
      return $two->attributes()['Location'];
    }
    return FALSE;
  }
  
  protected function idpLogoutUrl($xml, &$post) {
    $post = -1;
    $metadata = $xml->children("urn:oasis:names:tc:SAML:2.0:metadata");
    $one = $metadata->IDPSSODescriptor->SingleLogoutService[0];
    $two = $metadata->IDPSSODescriptor->SingleLogoutService[1];
    if ($one->attributes()['Binding'] == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect') {
      $post = FALSE;
    }
    if ($one->attributes()['Binding'] == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST') {
      $post = TRUE;
    }
    if ($post !== -1) {
      return $one->attributes()['Location'];
    }
    if ($two->attributes()['Binding'] == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect') {
      $post = FALSE;
    }
    if ($two->attributes()['Binding'] == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST') {
      $post = TRUE;
    }
    if ($post !== -1) {
      return $two->attributes()['Location'];
    }
    return FALSE;
  }

  /**
   * @TODO [microspid_idp_response_url description]
   * @method microspid_idp_response_url
   * @param  [type] $xml
   *   [description].
   * @param  [type] $post
   *   [description].
   * @return [type]                           [description]
   */
  protected function idpResponseUrl($xml, &$post) {
    $post = -1;
    $metadata = $xml->children("urn:oasis:names:tc:SAML:2.0:metadata");
    $one = $metadata->IDPSSODescriptor->SingleLogoutService[0];
    $two = $metadata->IDPSSODescriptor->SingleLogoutService[1];
    if ($one->attributes()['Binding'] == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect') {
      $post = FALSE;
    }
    if ($one->attributes()['Binding'] == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST') {
      $post = TRUE;
    }
    $check = $_SERVER['REQUEST_METHOD'] === 'POST' ? TRUE : FALSE;
    if ($post === $check) {
      return isset($one->attributes()['ResponseLocation']) ? $one->attributes()['ResponseLocation'] : $one->attributes()['Location'];
    }
    if ($two->attributes()['Binding'] == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect') {
      $post = FALSE;
    }
    if ($two->attributes()['Binding'] == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST') {
      $post = TRUE;
    }
    if ($post !== -1) {
      return isset($two->attributes()['ResponseLocation']) ? $two->attributes()['ResponseLocation'] : $two->attributes()['Location'];
    }
    return FALSE;
  }

  protected function canonicalizeData($node, $arXPath = NULL, $prefixList = NULL) {
    $exclusive = TRUE;
    $withComments = FALSE;

    if (is_null($arXPath) && ($node instanceof DOMNode) && ($node->ownerDocument !== NULL) && $node->isSameNode($node->ownerDocument->documentElement)) {
      /* Check for any PI or comments as they would have been excluded */
      $element = $node;
      while ($refnode = $element->previousSibling) {
        if ($refnode->nodeType == XML_PI_NODE || (($refnode->nodeType == XML_COMMENT_NODE) && $withComments)) {
          break;
        }
        $element = $refnode;
      }
      if ($refnode == NULL) {
        $node = $node->ownerDocument;
      }
    }

    return $node->C14N($exclusive, $withComments, $arXPath, $prefixList);
  }

  protected function selectAlgo($uri) {
    switch ($uri) {
      case 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256':
        return OPENSSL_ALGO_SHA256;

      case 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384':
        return OPENSSL_ALGO_SHA384;

      case 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512':
        return OPENSSL_ALGO_SHA512;

      default:
        return FALSE;
    }
  }

  /**
   * @TODO [microspid_validate_sign description]
   * @method microspid_validate_sign
   * @param  [type] $xml
   *   [description].
   * @param  [type] $cert
   *   [description].
   * @return [type]                        [description]
   */
  public function validateSign($xml, $cert) {
    $dom = new \DOMDocument();
    $oldEntityLoader = libxml_disable_entity_loader(TRUE);
    $res = $dom->loadXML($xml);
    libxml_disable_entity_loader($oldEntityLoader);
    $rootNode = $dom->firstChild;
    $xpath = new \DOMXPath($dom);
    $xpath->registerNamespace('secdsig', 'http://www.w3.org/2000/09/xmldsig#');
    $query = ".//secdsig:Signature";
    $nodeset = $xpath->query($query, $rootNode);
    $sigNode = $nodeset->item(0);
    $query = "string(./secdsig:SignedInfo/secdsig:SignatureMethod/@Algorithm)";
    $algorithm = $xpath->evaluate($query, $sigNode);
    $algo = $this->selectAlgo($algorithm);
    if ($algo === FALSE) {
      return t('Invalid algorithm (') . $algorithm . ')';
    }
    $query = "./secdsig:SignedInfo";
    $nodeset = $xpath->query($query, $sigNode);
    if ($signInfoNode = $nodeset->item(0)) {
      $signedInfo = $this->canonicalizeData($signInfoNode);
    }
    $docElem = $dom->documentElement;
    if (!$docElem->isSameNode($sigNode)) {
      $sigNode->parentNode->removeChild($sigNode);
    }
    $query = "./secdsig:SignedInfo/secdsig:Reference";
    $nodeset = $xpath->query($query, $sigNode);
    $refNode = $nodeset->item(0);

    $query = './secdsig:Transforms/secdsig:Transform';
    $nodelist = $xpath->query($query, $refNode);
    $arXPath = NULL;
    $prefixList = NULL;
    foreach ($nodelist as $transform) {
      $node = $transform->firstChild;
      while ($node) {
        if ($node->localName == 'InclusiveNamespaces') {
          if ($pfx = $node->getAttribute('PrefixList')) {
            $arpfx = array();
            $pfxlist = explode(" ", $pfx);
            foreach ($pfxlist as $pfx) {
              $val = trim($pfx);
              if (!empty($val)) {
                $arpfx[] = $val;
              }
            }
            if (count($arpfx) > 0) {
              $prefixList = $arpfx;
            }
          }
          break;
        }
        $node = $node->nextSibling;
      }
    }

    $result = FALSE;
    if ($uri = $refNode->getAttribute("URI")) {
      $arUrl = parse_url($uri);
      if (empty($arUrl['path'])) {
        if (array_key_exists('fragment', $arUrl) && $identifier = $arUrl['fragment']) {
          $iDlist = '@Id="' . $identifier . '"';
          $iDlist .= " or @ID='$identifier'";
          $query = '//*[' . $iDlist . ']';
          $dataObject = $xpath->query($query)->item(0);
          $data = $this->canonicalizeData($dataObject, $arXPath, $prefixList);
          $query = 'string(./secdsig:DigestMethod/@Algorithm)';
          $digestAlgorithm = $xpath->evaluate($query, $refNode);
          switch ($digestAlgorithm) {
            case 'http://www.w3.org/2001/04/xmlenc#sha256':
              $alg = 'sha256';
              break;

            case 'http://www.w3.org/2001/04/xmldsig-more#sha384':
              $alg = 'sha384';
              break;

            case 'http://www.w3.org/2001/04/xmlenc#sha512':
              $alg = 'sha512';
              break;

            default:
              throw new Exception("Cannot validate digest: Unsupported Algorithm <$digestAlgorithm>");
          }
          $digest = hash($alg, $data, TRUE);
          $query = 'string(./secdsig:DigestValue)';
          $digestValue = $xpath->evaluate($query, $refNode);
          if ($digest != base64_decode($digestValue)) {
            return t('Digest value is not valid');
          }
          else {
            $result = TRUE;
          }
        }
      }
    }

    if ($result === FALSE) {
      return FALSE;
    }

    $query = "string(./secdsig:SignatureValue)";
    $sigValue = $xpath->evaluate($query, $sigNode);

    $cert = "-----BEGIN CERTIFICATE-----\n" . trim($cert) . "\n-----END CERTIFICATE-----\n";

    return openssl_verify($signedInfo, base64_decode($sigValue), $cert, $algo) == 1 ? TRUE : t('Invalid signature');
  }

  /**
   * @TODO [microspid_validate_sign_redirect description]
   * @method microspid_validate_sign_redirect
   * @param  [type] $query
   *   [description].
   * @param  [type] $cert
   *   [description].
   * @return [type]                                  [description]
   */
  public function validateSignRedirect($query, $cert) {
    $pos = strpos($query, '&Signature');
    if ($pos === FALSE) {
      return FALSE;
    }
    $check = substr($query, 0, $pos);
    $sigValue = urldecode(substr($query, $pos + 11));
    $pos = strpos($check, '&SigAlg');
    $algorithm = urldecode(substr($check, $pos + 8));
    $algo = $this->selectAlgo($algorithm);
    if ($algo === FALSE) {
      return t('Invalid algorithm (') . $algorithm . ')';
    }

    $cert = "-----BEGIN CERTIFICATE-----\n" . trim($cert) . "\n-----END CERTIFICATE-----\n";

    return openssl_verify($check, base64_decode($sigValue), $cert, $algo) == 1 ? TRUE : t('Invalid signature');
  }
  
  /**
   * @TODO [microspid_add_sign description]
   * @method microspid_add_sign
   * @param  [type] $xml
   *   [description].
   * @param  [type] $cert
   *   [description].
   */
  public function addSign($xml, $cert) {
    $dom = new \DOMDocument();
    $oldEntityLoader = libxml_disable_entity_loader(TRUE);
    $res = $dom->loadXML($xml);
    libxml_disable_entity_loader($oldEntityLoader);
    $rootNode = $dom->firstChild;
    // print_r($rootNode);exit;
    $template = '
  <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
      <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
    </ds:SignedInfo>
    <ds:KeyInfo>
      <ds:X509Data>
        <ds:X509Certificate></ds:X509Certificate>
      </ds:X509Data>
    </ds:KeyInfo>
  </ds:Signature>';
    $xmldsig = 'http://www.w3.org/2000/09/xmldsig#';
    $sigdoc = new \DOMDocument();
    $sigdoc->loadXML($template);
    $sigNode = $sigdoc->documentElement;

    $xpath = new \DOMXPath($sigdoc);
    $xpath->registerNamespace('secdsig', $xmldsig);
    $query = "./secdsig:SignedInfo";
    $nodeset = $xpath->query($query, $sigNode);
    $infoNode = $nodeset->item(0);

    $refNode = $sigdoc->createElementNS($xmldsig, 'ds:Reference');
    $infoNode->appendChild($refNode);
    $uri = $rootNode->getAttribute('ID');
    $refNode->setAttribute("URI", '#' . $uri);
    // Echo $sigdoc->saveXML();exit;
    $transNodes = $sigdoc->createElementNS($xmldsig, 'ds:Transforms');
    $refNode->appendChild($transNodes);
    $transNode = $sigdoc->createElementNS($xmldsig, 'ds:Transform');
    $transNodes->appendChild($transNode);
    $transNode->setAttribute('Algorithm', "http://www.w3.org/2000/09/xmldsig#enveloped-signature");
    $transNode = $sigdoc->createElementNS($xmldsig, 'ds:Transform');
    $transNodes->appendChild($transNode);
    $transNode->setAttribute('Algorithm', "http://www.w3.org/2001/10/xml-exc-c14n#");
    $canonicalData = $this->canonicalizeData($rootNode);
    $digValue = base64_encode(hash("sha256", $canonicalData, TRUE));

    $digestMethod = $sigdoc->createElementNS($xmldsig, 'ds:DigestMethod');
    $refNode->appendChild($digestMethod);
    $digestMethod->setAttribute('Algorithm', "http://www.w3.org/2001/04/xmlenc#sha256");

    $digestValue = $sigdoc->createElementNS($xmldsig, 'ds:DigestValue', $digValue);
    $refNode->appendChild($digestValue);

    $data = $this->canonicalizeData($infoNode);
    $certsManager = \Drupal::service('microspid.certs.manager');

    $pKeyPath = $certsManager->getPrivateKeyPath();
    if ($this->config->get('debug')) {
      \Drupal::logger('microspid')->debug('pkey path: %path', array('%path' => $pKeyPath));
    }

    // Fetch private key from file and ready it.
    $pKeyId = openssl_pkey_get_private('file://' . $pKeyPath);
    if (!$pKeyId) {
      \Drupal::logger('microspid')->error('failure on read private key');
      exit();
    }
    // Compute signature.
    openssl_sign($data, $signature, $pKeyId, OPENSSL_ALGO_SHA256);
    // Free the key from memory.
    openssl_free_key($pKeyId);
    $sigValue = base64_encode($signature);

    $sigValueNode = $sigdoc->createElementNS($xmldsig, 'ds:SignatureValue', $sigValue);
    if ($infoSibling = $infoNode->nextSibling) {
      $infoSibling->parentNode->insertBefore($sigValueNode, $infoSibling);
    }
    else {
      $sigNode->appendChild($sigValueNode);
    }

    $query = "./secdsig:KeyInfo/secdsig:X509Data/secdsig:X509Certificate";
    $nodeset = $xpath->query($query, $sigNode);
    $certNode = $nodeset->item(0);
    $certNode->nodeValue = $cert;

    $signatureElement = $dom->importNode($sigNode, TRUE);
    $insertBefore = $rootNode->firstChild;
    $messageTypes = array('AuthnRequest', 'Response', 'LogoutRequest', 'LogoutResponse');
    if (in_array($rootNode->localName, $messageTypes)) {
      $issuerNodes = $this->query($dom, '/' . $rootNode->tagName . '/saml:Issuer');
      if ($issuerNodes->length == 1) {
        $insertBefore = $issuerNodes->item(0)->nextSibling;
      }
    }

    $rootNode->insertBefore($signatureElement, $insertBefore);

    return $dom;
  }

  /**
   * @TODO [microspid_post description]
   * @method microspid_post
   * @param  [type] $xml
   *   [description].
   * @param  [type] $url
   *   [description].
   * @param  [type] $rs
   *   [description].
   * @param  bool $response
   *   [description].
   * @return [type]                   [description]
   */

  public function post($xml, $url, $rs, $response = FALSE) {
    $path = $this->config->get('privatepath');
    if (empty($path)) {
      $path = \Drupal::service('file_system')->realpath('private://microspid') . '/cert';
    }
    $certsManager = \Drupal::service('microspid.certs.manager');
    $cert = $certsManager->getCert($path.'/spid-sp.crt');
    $dom = $this->addSign($xml, $cert);

    if ($this->config->get('debug')) {
      \Drupal::logger('microspid')->debug('xml: %xml', array('%xml' => $dom->saveXML()));
    }

    $data = base64_encode($dom->saveXML());
    $action = $response ? "SAMLResponse" : "SAMLRequest";
    // $rs = microspid_get_relaystate(); // '_' . md5(uniqid(mt_rand(), true));//.
    $input = $rs === NULL ? '' : "<input type=\"hidden\" name=\"RelayState\" value=\"$rs\">";

    $page = <<<PAGINA
  <html>
    <body onload="javascript:document.forms[0].submit()">
      Attendere...
      <form method="post" action="$url">
        $input
        <input type="hidden" name="$action" value="$data">
        <input type="submit" style="display:none" value="Go"/>
      </form>
    </body>
  </html>
PAGINA;
    exit($page);
  }

  public function query($dom, $query, $context = NULL) {
    $xpath = new \DOMXPath($dom);
    $xpath->registerNamespace('samlp', 'urn:oasis:names:tc:SAML:2.0:protocol');
    $xpath->registerNamespace('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
    $xpath->registerNamespace('ds', 'http://www.w3.org/2000/09/xmldsig#');
    if (isset($context)) {
      $res = $xpath->query($query, $context);
    }
    else {
      $res = $xpath->query($query);
    }
    return $res;
  }

  /**
   * @TODO [microspid_saml2unix_ts description]
   * @method microspid_saml2unix_ts
   * @param  [type] $time
   *   [description].
   * @return [type]                       [description]
   */
  function samlToUnixTime($time) {
    $matches = array();
/*
    if (strlen($time) != 20) {
      return 0;
    }
*/
    // We use a very strict regex to parse the timestamp.
    $regex = '/^(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)T(\\d\\d):(\\d\\d):(\\d\\d)(?:\\.\\d+)?Z$/D';
    try {
      $result = preg_match($regex, $time, $matches);
    }
    catch (\Exception $ex) {
      $result = 0;
    }
    if ($result == 0) {
      return 0;
    }
    // Extract the different components of the time from the  matches in the regex.
    // intval will ignore leading zeroes in the string.
    $year   = intval($matches[1]);
    $month  = intval($matches[2]);
    $day    = intval($matches[3]);
    $hour   = intval($matches[4]);
    $minute = intval($matches[5]);
    $second = intval($matches[6]);

    // We use gmmktime because the timestamp will always be given
    // in UTC.
    $ts = gmmktime($hour, $minute, $second, $month, $day, $year);

    return $ts;
  }

  /**
   * @TODO [microspid_agid_error description]
   * @method microspid_agid_error
   * @param  [type] $error_code
   *   [description].
   * @return [type]                           [description]
   */
  protected function agidError($error_code) {
    $error_code_filtered = \Drupal\Component\Utility\Xss::filter($error_code);
    switch ($error_code) {
      case 'ErrorCode nr08':
        return $error_code . t('. Not a SAML request.');

      case 'ErrorCode nr09':
        return $error_code . t('. Version parameter incorrect.');

      case 'ErrorCode nr11':
        return $error_code . t('. ID incorrect.');

      case 'ErrorCode nr12':
        return $error_code . t('. RequestAuthnContext incorrect.');

      case 'ErrorCode nr13':
        return $error_code . t('. IssueInstant incorrect.');

      case 'ErrorCode nr14':
        return $error_code . t('. destination incorrect.');

      case 'ErrorCode nr15':
        return $error_code . t('. isPassive incorrect.');

      case 'ErrorCode nr16':
        return $error_code . t('. AssertionConsumerService incorrect.');

      case 'ErrorCode nr17':
        return $error_code . t('. NameIDPolicy/Format element incorrect.');

      case 'ErrorCode nr18':
        return $error_code . t('. AttributeConsumerServiceIndex incorrect.');

      case 'ErrorCode nr19':
        return $error_code . t('. Credentials incorrect.');

      case 'ErrorCode nr20':
        return $error_code . t('. Level mismatch.');

      case 'ErrorCode nr21':
        return $error_code . t('. Timeout.');

      case 'ErrorCode nr22':
        return $error_code . t('. User denies consent.');

      case 'ErrorCode nr23':
        return $error_code . t('. User credendials blocked.');

      case 'ErrorCode nr25':
        return $error_code . t('. User cancels request.');

      default:
        return $error_code_filtered . t('. Unknown issue.');
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc