fetch() public static method

. An exception will be thrown if we are unable to retrieve the data.
Author: Andjelko Horvat
Author: Olav Morken, UNINETT AS (olav.morken@uninett.no)
Author: Marco Ferrante, University of Genova (marco@csita.unige.it)
public static fetch ( string $url, array $context = [], boolean $getHeaders = false ) : mixed
$url string The path or URL we should fetch.
$context array Extra context options. This parameter is optional.
$getHeaders boolean Whether to also return response headers. Optional.
return mixed array if $getHeaders is set, string otherwise
Example #1
0
 public function finalStep(&$state)
 {
     SimpleSAML_Logger::debug("oauth wrap:  Using this verification code [" . $state['authwindowslive:wrap_verification_code'] . "]");
     // Retrieve Access Token
     // Documentation at: http://msdn.microsoft.com/en-us/library/ff749686.aspx
     $postData = 'wrap_client_id=' . urlencode($this->key) . '&wrap_client_secret=' . urlencode($this->secret) . '&wrap_callback=' . urlencode(SimpleSAML_Module::getModuleUrl('authwindowslive') . '/linkback.php') . '&wrap_verification_code=' . urlencode($state['authwindowslive:wrap_verification_code']);
     $context = array('http' => array('method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', 'content' => $postData));
     $result = \SimpleSAML\Utils\HTTP::fetch('https://consent.live.com/AccessToken.aspx', $context);
     parse_str($result, $response);
     // error checking of $response to make sure we can proceed
     if (!array_key_exists('wrap_access_token', $response)) {
         throw new Exception('[' . $response['error_code'] . '] ' . $response['wrap_error_reason'] . "\r\nNo wrap_access_token returned - cannot proceed\r\n" . $response['internal_info']);
     }
     SimpleSAML_Logger::debug("Got an access token from the OAuth WRAP service provider [" . $response['wrap_access_token'] . "] for user [" . $response['uid'] . "]");
     // Documentation at: http://msdn.microsoft.com/en-us/library/ff751708.aspx
     $opts = array('http' => array('header' => "Accept: application/json\r\nAuthorization: WRAP access_token=" . $response['wrap_access_token'] . "\r\n"));
     $data = \SimpleSAML\Utils\HTTP::fetch('https://apis.live.net/V4.1/cid-' . $response['uid'] . '/Profiles', $opts);
     $userdata = json_decode($data, TRUE);
     $attributes = array();
     $attributes['windowslive_uid'] = array($response['uid']);
     $attributes['windowslive_targetedID'] = array('http://windowslive.com!' . $response['uid']);
     $attributes['windowslive_user'] = array($response['uid'] . '@windowslive.com');
     if (array_key_exists('Entries', $userdata)) {
         foreach ($userdata['Entries'][0] as $key => $value) {
             if (is_string($value)) {
                 $attributes['windowslive.' . $key] = array((string) $value);
             }
         }
         if (array_key_exists('Emails', $userdata['Entries'][0])) {
             $attributes['windowslive_mail'] = array($userdata['Entries'][0]['Emails'][0]['Address']);
         }
     }
     SimpleSAML_Logger::debug('LiveID Returned Attributes: ' . implode(", ", array_keys($attributes)));
     $state['Attributes'] = $attributes;
 }
Example #2
0
 /**
  * @param $state
  *
  * @throws Exception
  */
 public function finalStep(&$state)
 {
     SimpleSAML\Logger::debug("authwindowslive oauth: Using this verification code [" . $state['authwindowslive:verification_code'] . "]");
     // retrieve Access Token
     // documentation at:
     // https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-protocols-oauth-code/#request-an-access-token
     $postData = 'client_id=' . urlencode($this->key) . '&client_secret=' . urlencode($this->secret) . '&scope=' . urlencode('https://graph.microsoft.com/user.read') . '&grant_type=authorization_code' . '&redirect_uri=' . urlencode(SimpleSAML\Module::getModuleUrl('authwindowslive') . '/linkback.php') . '&code=' . urlencode($state['authwindowslive:verification_code']);
     $context = array('http' => array('method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', 'content' => $postData));
     $result = \SimpleSAML\Utils\HTTP::fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', $context);
     $response = json_decode($result, true);
     // error checking of $response to make sure we can proceed
     if (!array_key_exists('access_token', $response)) {
         throw new Exception('[' . $response['error'] . '] ' . $response['error_description'] . "\r\nNo access_token returned - cannot proceed\r\n" . implode(', ', $response['error_codes']));
     }
     SimpleSAML\Logger::debug("authwindowslive: Got an access token from the OAuth service provider [" . $response['access_token'] . "]");
     // documentation at: http://graph.microsoft.io/en-us/docs/overview/call_api
     $opts = array('http' => array('header' => "Accept: application/json\r\nAuthorization: Bearer " . $response['access_token'] . "\r\n"));
     $data = \SimpleSAML\Utils\HTTP::fetch('https://graph.microsoft.com/v1.0/me', $opts);
     $userdata = json_decode($data, true);
     // this is the simplest case
     if (!array_key_exists('@odata.context', $userdata) || array_key_exists('error', $userdata)) {
         throw new Exception('Unable to retrieve userdata from Microsoft Graph [' . $userdata['error']['code'] . '] ' . $userdata['error']['message']);
     }
     $attributes = array();
     $attributes['windowslive_targetedID'] = array('https://graph.microsoft.com!' . (!empty($userdata['id']) ? $userdata['id'] : 'unknown'));
     foreach ($userdata as $key => $value) {
         if (is_string($value)) {
             $attributes['windowslive.' . $key] = array((string) $value);
         }
     }
     SimpleSAML\Logger::debug('LiveID Returned Attributes: ' . implode(", ", array_keys($attributes)));
     $state['Attributes'] = $attributes;
 }
Example #3
0
 /**
  * This function receives a SAML 1.1 artifact.
  *
  * @param SimpleSAML_Configuration $spMetadata  The metadata of the SP.
  * @param SimpleSAML_Configuration $idpMetadata  The metadata of the IdP.
  * @return string  The <saml1p:Response> element, as an XML string.
  */
 public static function receive(SimpleSAML_Configuration $spMetadata, SimpleSAML_Configuration $idpMetadata)
 {
     $artifacts = self::getArtifacts();
     $request = self::buildRequest($artifacts);
     \SimpleSAML\Utils\XML::debugSAMLMessage($request, 'out');
     $url = $idpMetadata->getDefaultEndpoint('ArtifactResolutionService', array('urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding'));
     $url = $url['Location'];
     $peerPublicKeys = $idpMetadata->getPublicKeys('signing', TRUE);
     $certData = '';
     foreach ($peerPublicKeys as $key) {
         if ($key['type'] !== 'X509Certificate') {
             continue;
         }
         $certData .= "-----BEGIN CERTIFICATE-----\n" . chunk_split($key['X509Certificate'], 64) . "-----END CERTIFICATE-----\n";
     }
     $file = SimpleSAML\Utils\System::getTempDir() . DIRECTORY_SEPARATOR . sha1($certData) . '.crt';
     if (!file_exists($file)) {
         SimpleSAML\Utils\System::writeFile($file, $certData);
     }
     $spKeyCertFile = \SimpleSAML\Utils\Config::getCertPath($spMetadata->getString('privatekey'));
     $opts = array('ssl' => array('verify_peer' => TRUE, 'cafile' => $file, 'local_cert' => $spKeyCertFile, 'capture_peer_cert' => TRUE, 'capture_peer_chain' => TRUE), 'http' => array('method' => 'POST', 'content' => $request, 'header' => 'SOAPAction: http://www.oasis-open.org/committees/security' . "\r\n" . 'Content-Type: text/xml'));
     // Fetch the artifact
     $response = \SimpleSAML\Utils\HTTP::fetch($url, $opts);
     if ($response === FALSE) {
         throw new SimpleSAML_Error_Exception('Failed to retrieve assertion from IdP.');
     }
     \SimpleSAML\Utils\XML::debugSAMLMessage($response, 'in');
     // Find the response in the SOAP message
     $response = self::extractResponse($response);
     return $response;
 }
Example #4
0
 /**
  * This function processes a SAML metadata file.
  *
  * @param $source
  */
 public function loadSource($source)
 {
     if (preg_match('@^https?://@i', $source['src'])) {
         // Build new HTTP context
         $context = $this->createContext($source);
         // GET!
         try {
             list($data, $responseHeaders) = \SimpleSAML\Utils\HTTP::fetch($source['src'], $context, TRUE);
         } catch (Exception $e) {
             SimpleSAML_Logger::warning('metarefresh: ' . $e->getMessage());
         }
         // We have response headers, so the request succeeded
         if (!isset($responseHeaders)) {
             // No response headers, this means the request failed in some way, so re-use old data
             SimpleSAML_Logger::debug('No response from ' . $source['src'] . ' - attempting to re-use cached metadata');
             $this->addCachedMetadata($source);
             return;
         } elseif (preg_match('@^HTTP/1\\.[01]\\s304\\s@', $responseHeaders[0])) {
             // 304 response
             SimpleSAML_Logger::debug('Received HTTP 304 (Not Modified) - attempting to re-use cached metadata');
             $this->addCachedMetadata($source);
             return;
         } elseif (!preg_match('@^HTTP/1\\.[01]\\s200\\s@', $responseHeaders[0])) {
             // Other error.
             SimpleSAML_Logger::debug('Error from ' . $source['src'] . ' - attempting to re-use cached metadata');
             $this->addCachedMetadata($source);
             return;
         }
     } else {
         /* Local file. */
         $data = file_get_contents($source['src']);
         $responseHeaders = NULL;
     }
     /* Everything OK. Proceed. */
     if (isset($source['conditionalGET']) && $source['conditionalGET']) {
         // Stale or no metadata, so a fresh copy
         SimpleSAML_Logger::debug('Downloaded fresh copy');
     }
     try {
         $entities = $this->loadXML($data, $source);
     } catch (Exception $e) {
         SimpleSAML_Logger::debug('XML parser error when parsing ' . $source['src'] . ' - attempting to re-use cached metadata');
         $this->addCachedMetadata($source);
         return;
     }
     foreach ($entities as $entity) {
         if (isset($source['blacklist'])) {
             if (!empty($source['blacklist']) && in_array($entity->getEntityID(), $source['blacklist'])) {
                 SimpleSAML_Logger::info('Skipping "' . $entity->getEntityID() . '" - blacklisted.' . "\n");
                 continue;
             }
         }
         if (isset($source['whitelist'])) {
             if (!empty($source['whitelist']) && !in_array($entity->getEntityID(), $source['whitelist'])) {
                 SimpleSAML_Logger::info('Skipping "' . $entity->getEntityID() . '" - not in the whitelist.' . "\n");
                 continue;
             }
         }
         if (array_key_exists('certificates', $source) && $source['certificates'] !== NULL) {
             if (!$entity->validateSignature($source['certificates'])) {
                 SimpleSAML_Logger::info('Skipping "' . $entity->getEntityId() . '" - could not verify signature using certificate.' . "\n");
                 continue;
             }
         }
         if (array_key_exists('validateFingerprint', $source) && $source['validateFingerprint'] !== NULL) {
             if (!array_key_exists('certificates', $source) || $source['certificates'] == NULL) {
                 if (!$entity->validateFingerprint($source['validateFingerprint'])) {
                     SimpleSAML_Logger::info('Skipping "' . $entity->getEntityId() . '" - could not verify signature using fingerprint.' . "\n");
                     continue;
                 }
             } else {
                 SimpleSAML_Logger::info('Skipping validation with fingerprint since option certificate is set.' . "\n");
             }
         }
         $template = NULL;
         if (array_key_exists('template', $source)) {
             $template = $source['template'];
         }
         $this->addMetadata($source['src'], $entity->getMetadata1xSP(), 'shib13-sp-remote', $template);
         $this->addMetadata($source['src'], $entity->getMetadata1xIdP(), 'shib13-idp-remote', $template);
         $this->addMetadata($source['src'], $entity->getMetadata20SP(), 'saml20-sp-remote', $template);
         $this->addMetadata($source['src'], $entity->getMetadata20IdP(), 'saml20-idp-remote', $template);
         $attributeAuthorities = $entity->getAttributeAuthorities();
         if (!empty($attributeAuthorities)) {
             $this->addMetadata($source['src'], $attributeAuthorities[0], 'attributeauthority-remote', $template);
         }
     }
     $this->saveState($source, $responseHeaders);
 }
Example #5
0
 /**
  * This function parses a file where the root node is either an EntityDescriptor element or an
  * EntitiesDescriptor element. In both cases it will return an associative array of SAMLParser instances. If
  * the file contains a single EntityDescriptorElement, then the array will contain a single SAMLParser
  * instance.
  *
  * @param string $file The path to the file which contains the EntityDescriptor or EntitiesDescriptor element.
  *
  * @return SimpleSAML_Metadata_SAMLParser[] An array of SAMLParser instances.
  * @throws Exception If the file does not parse as XML.
  */
 public static function parseDescriptorsFile($file)
 {
     if ($file === null) {
         throw new Exception('Cannot open file NULL. File name not specified.');
     }
     $data = \SimpleSAML\Utils\HTTP::fetch($file);
     $doc = new DOMDocument();
     $res = $doc->loadXML($data);
     if ($res !== true) {
         throw new Exception('Failed to read XML from file: ' . $file);
     }
     if ($doc->documentElement === null) {
         throw new Exception('Opened file is not an XML document: ' . $file);
     }
     return self::parseDescriptorsElement($doc->documentElement);
 }
 /**
  * This function parses a file where the root node is either an EntityDescriptor element or an
  * EntitiesDescriptor element. In both cases it will return an associative array of SAMLParser instances. If
  * the file contains a single EntityDescriptorElement, then the array will contain a single SAMLParser
  * instance.
  *
  * @param string $file The path to the file which contains the EntityDescriptor or EntitiesDescriptor element.
  *
  * @return SimpleSAML_Metadata_SAMLParser[] An array of SAMLParser instances.
  * @throws Exception If the file does not parse as XML.
  */
 public static function parseDescriptorsFile($file)
 {
     if ($file === null) {
         throw new Exception('Cannot open file NULL. File name not specified.');
     }
     $data = \SimpleSAML\Utils\HTTP::fetch($file);
     try {
         $doc = \SAML2\DOMDocumentFactory::fromString($data);
     } catch (\Exception $e) {
         throw new Exception('Failed to read XML from file: ' . $file);
     }
     if ($doc->documentElement === null) {
         throw new Exception('Opened file is not an XML document: ' . $file);
     }
     return self::parseDescriptorsElement($doc->documentElement);
 }
Example #7
0
 /**
  * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::fetch() instead.
  */
 public static function fetch($path, $context = array(), $getHeaders = FALSE)
 {
     return \SimpleSAML\Utils\HTTP::fetch($path, $context, $getHeaders);
 }
Example #8
0
} else {
    $type = 'definition';
}
$basefile = basename($fileWithoutExt);
echo 'Action: [' . $action . ']' . "\n";
echo 'Application: [' . $application . ']' . "\n";
echo 'File orig: [' . $file . ']' . "\n";
echo 'File base: [' . $basefile . ']' . "\n";
switch ($action) {
    case 'pulldef':
        $content = \SimpleSAML\Utils\HTTP::fetch($base . 'export.php?aid=' . $application . '&type=def&file=' . $basefile);
        file_put_contents($fileWithoutExt . '.definition.json', $content);
        break;
    case 'pull':
        try {
            $content = \SimpleSAML\Utils\HTTP::fetch($base . 'export.php?aid=' . $application . '&type=translation&file=' . $basefile);
            file_put_contents($fileWithoutExt . '.translation.json', $content);
        } catch (SimpleSAML_Error_Exception $e) {
            echo 'Translation unavailable for ' . $basefile;
            SimpleSAML_Logger::warning("Translation unavailable for {$basefile} in {$base}: " . $e->getMessage());
        }
        break;
    case 'push':
        push($file, $basefile, $application, $type);
        break;
    case 'convert':
        include $file;
        $definition = json_format(convert_definition($lang));
        $translation = json_format(convert_translation($lang)) . "\n";
        file_put_contents($fileWithoutExt . '.definition.json', $definition);
        file_put_contents($fileWithoutExt . '.translation.json', $translation);
Example #9
0
 /**
  * Uses the cas service validate, this provides additional attributes
  *
  * @param string $ticket
  * @param string $service
  * @return list username and attributes
  */
 private function casServiceValidate($ticket, $service)
 {
     $url = \SimpleSAML\Utils\HTTP::addURLParameters($this->_casConfig['serviceValidate'], array('ticket' => $ticket, 'service' => $service));
     $result = \SimpleSAML\Utils\HTTP::fetch($url);
     $dom = DOMDocument::loadXML($result);
     $xPath = new DOMXpath($dom);
     $xPath->registerNamespace("cas", 'http://www.yale.edu/tp/cas');
     $success = $xPath->query("/cas:serviceResponse/cas:authenticationSuccess/cas:user");
     if ($success->length == 0) {
         $failure = $xPath->evaluate("/cas:serviceResponse/cas:authenticationFailure");
         throw new Exception("Error when validating CAS service ticket: " . $failure->item(0)->textContent);
     } else {
         $attributes = array();
         if ($casattributes = $this->_casConfig['attributes']) {
             # some has attributes in the xml - attributes is a list of XPath expressions to get them
             foreach ($casattributes as $name => $query) {
                 $attrs = $xPath->query($query);
                 foreach ($attrs as $attrvalue) {
                     $attributes[$name][] = $attrvalue->textContent;
                 }
             }
         }
         $casusername = $success->item(0)->textContent;
         return array($casusername, $attributes);
     }
 }
try {
    // Load SimpleSAMLphp, configuration and metadata
    $casconfig = SimpleSAML_Configuration::getConfig('module_casserver.php');
    $path = $casconfig->resolvePath($casconfig->getValue('ticketcache', 'ticketcache'));
    $ticketcontent = retrieveTicket($ticket, $path);
    $usernamefield = $casconfig->getValue('attrname', 'eduPersonPrincipalName');
    $dosendattributes = $casconfig->getValue('attributes', FALSE);
    $attributes = $ticketcontent['attributes'];
    $pgtiouxml = "";
    if ($ticketcontent['service'] == $service && $ticketcontent['forceAuthn'] == $forceAuthn && array_key_exists($usernamefield, $attributes) && $ticketcontent['validbefore'] > time()) {
        if (isset($_GET['pgtUrl'])) {
            $pgtUrl = $_GET['pgtUrl'];
            $pgtiou = str_replace('_', 'PGTIOU-', SimpleSAML\Utils\Random::generateID());
            $pgt = str_replace('_', 'PGT-', SimpleSAML\Utils\Random::generateID());
            $content = array('attributes' => $attributes, 'forceAuthn' => false, 'proxies' => array_merge(array($service), $ticketcontent['proxies']), 'validbefore' => time() + 60);
            \SimpleSAML\Utils\HTTP::fetch($pgtUrl . '?pgtIou=' . $pgtiou . '&pgtId=' . $pgt);
            storeTicket($pgt, $path, $content);
            $pgtiouxml = "\n<cas:proxyGrantingTicket>{$pgtiou}</cas:proxyGrantingTicket>\n";
        }
        $proxiesxml = join("\n", array_map(create_function('$a', 'return "<cas:proxy>$a</cas:proxy>";'), $ticketcontent['proxies']));
        if ($proxiesxml) {
            $proxiesxml = "<cas:proxies>\n{$proxiesxml}\n</cas:proxies>\n";
        }
        returnResponse('YES', $function, $attributes[$usernamefield][0], $dosendattributes ? $attributes : array(), $pgtiouxml . $proxiesxml);
    } else {
        returnResponse('NO', $function);
    }
} catch (Exception $e) {
    returnResponse('NO', $function, $e->getMessage());
}
function returnResponse($value, $function, $usrname = '', $attributes = array(), $xtraxml = "")
 /**
  * Overriding this function from the superclass SimpleSAML_Metadata_MetaDataStorageSource.
  *
  * This function retrieves metadata for the given entity id in the given set of metadata.
  * It will return NULL if it is unable to locate the metadata.
  *
  * This class implements this function using the getMetadataSet-function. A subclass should
  * override this function if it doesn't implement the getMetadataSet function, or if the
  * implementation of getMetadataSet is slow.
  *
  * @param $index  The entityId or metaindex we are looking up.
  * @param $set  The set we are looking for metadata in.
  * @return An associative array with metadata for the given entity, or NULL if we are unable to
  *         locate the entity.
  */
 public function getMetaData($index, $set)
 {
     assert('is_string($index)');
     assert('is_string($set)');
     SimpleSAML_Logger::info('MetaData - Handler.MDX: Loading metadata entity [' . $index . '] from [' . $set . ']');
     /* Read from cache if possible. */
     $data = $this->getFromCache($set, $index);
     if ($data !== NULL && array_key_exists('expires', $data) && $data['expires'] < time()) {
         /* Metadata has expired. */
         $data = NULL;
     }
     if (isset($data)) {
         /* Metadata found in cache and not expired. */
         SimpleSAML_Logger::debug('MetaData - Handler.MDX: Using cached metadata for: ' . $index . '.');
         return $data;
     }
     /* Look at Metadata Query Protocol: https://github.com/iay/md-query/blob/master/draft-young-md-query.txt */
     $mdx_url = $this->server . '/entities/' . urlencode($index);
     SimpleSAML_Logger::debug('MetaData - Handler.MDX: Downloading metadata for "' . $index . '" from [' . $mdx_url . ']');
     try {
         $xmldata = \SimpleSAML\Utils\HTTP::fetch($mdx_url);
     } catch (Exception $e) {
         SimpleSAML_Logger::warning('Fetching metadata for ' . $index . ': ' . $e->getMessage());
     }
     if (empty($xmldata)) {
         $error = error_get_last();
         throw new Exception('Error downloading metadata for "' . $index . '" from "' . $mdx_url . '": ' . $error['message']);
     }
     $entity = SimpleSAML_Metadata_SAMLParser::parseString($xmldata);
     SimpleSAML_Logger::debug('MetaData - Handler.MDX: Completed parsing of [' . $mdx_url . ']');
     if ($this->validateFingerprint !== NULL) {
         if (!$entity->validateFingerprint($this->validateFingerprint)) {
             throw new Exception('Error, could not verify signature for entity: ' . $index . '".');
         }
     }
     $data = self::getParsedSet($entity, $set);
     if ($data === NULL) {
         throw new Exception('No metadata for set "' . $set . '" available from "' . $index . '".');
     }
     $this->writeToCache($set, $index, $data);
     return $data;
 }
Example #12
0
 /**
  * Overriding this function from the superclass SimpleSAML_Metadata_MetaDataStorageSource.
  *
  * This function retrieves metadata for the given entity id in the given set of metadata.
  * It will return NULL if it is unable to locate the metadata.
  *
  * This class implements this function using the getMetadataSet-function. A subclass should
  * override this function if it doesn't implement the getMetadataSet function, or if the
  * implementation of getMetadataSet is slow.
  *
  * @param string $index The entityId or metaindex we are looking up.
  * @param string $set The set we are looking for metadata in.
  *
  * @return array An associative array with metadata for the given entity, or NULL if we are unable to
  *         locate the entity.
  * @throws \Exception If an error occurs while downloading metadata, validating the signature or writing to cache.
  */
 public function getMetaData($index, $set)
 {
     assert('is_string($index)');
     assert('is_string($set)');
     Logger::info(__CLASS__ . ': loading metadata entity [' . $index . '] from [' . $set . ']');
     // read from cache if possible
     $data = $this->getFromCache($set, $index);
     if ($data !== null && array_key_exists('expires', $data) && $data['expires'] < time()) {
         // metadata has expired
         $data = null;
     }
     if (isset($data)) {
         // metadata found in cache and not expired
         Logger::debug(__CLASS__ . ': using cached metadata for: ' . $index . '.');
         return $data;
     }
     // look at Metadata Query Protocol: https://github.com/iay/md-query/blob/master/draft-young-md-query.txt
     $mdq_url = $this->server . '/entities/' . urlencode($index);
     Logger::debug(__CLASS__ . ': downloading metadata for "' . $index . '" from [' . $mdq_url . ']');
     try {
         $xmldata = HTTP::fetch($mdq_url);
     } catch (\Exception $e) {
         Logger::warning('Fetching metadata for ' . $index . ': ' . $e->getMessage());
     }
     if (empty($xmldata)) {
         $error = error_get_last();
         throw new \Exception('Error downloading metadata for "' . $index . '" from "' . $mdq_url . '": ' . $error['message']);
     }
     /** @var string $xmldata */
     $entity = \SimpleSAML_Metadata_SAMLParser::parseString($xmldata);
     Logger::debug(__CLASS__ . ': completed parsing of [' . $mdq_url . ']');
     if ($this->validateFingerprint !== null) {
         if (!$entity->validateFingerprint($this->validateFingerprint)) {
             throw new \Exception(__CLASS__ . ': error, could not verify signature for entity: ' . $index . '".');
         }
     }
     $data = self::getParsedSet($entity, $set);
     if ($data === null) {
         throw new \Exception(__CLASS__ . ': no metadata for set "' . $set . '" available from "' . $index . '".');
     }
     $this->writeToCache($set, $index, $data);
     return $data;
 }