Exemplo n.º 1
0
 /**
  * Apply this filter.
  *
  * @param array &$request  The current request
  */
 public function process(&$request)
 {
     assert('is_array($request)');
     assert('array_key_exists("Attributes", $request)');
     $attributes =& $request['Attributes'];
     if (!isset($attributes[$this->sourceAttribute])) {
         return;
     }
     // will not overwrite existing attribute
     if (isset($attributes[$this->targetAttribute])) {
         return;
     }
     $sourceAttrVal = $attributes[$this->sourceAttribute][0];
     /* the last position of an @ is usually the beginning of the scope
      * string */
     $scopeIndex = strrpos($sourceAttrVal, '@');
     if ($scopeIndex !== FALSE) {
         $attributes[$this->targetAttribute] = array();
         $scope = substr($sourceAttrVal, $scopeIndex + 1);
         $attributes[$this->targetAttribute][] = $scope;
         SimpleSAML\Logger::debug('ScopeFromAttribute: Inserted new attribute ' . $this->targetAttribute . ', with scope ' . $scope);
     } else {
         SimpleSAML\Logger::warning('ScopeFromAttribute: The configured source attribute ' . $this->sourceAttribute . ' does not have a scope. Did not add attribute ' . $this->targetAttribute . '.');
     }
 }
Exemplo n.º 2
0
 /**
  * Attempt to log in using the given username and password.
  *
  * On a successful login, this function should return the username as 'uid' attribute,
  * and merged attributes from the configuration file.
  * On failure, it should throw an exception. A SimpleSAML_Error_Error('WRONGUSERPASS')
  * should be thrown in case of a wrong username OR a wrong password, to prevent the
  * enumeration of usernames.
  *
  * @param string $username  The username the user wrote.
  * @param string $password  The password the user wrote.
  * @return array  Associative array with the users attributes.
  */
 protected function login($username, $password)
 {
     assert('is_string($username)');
     assert('is_string($password)');
     foreach ($this->users as $userpass) {
         $matches = explode(':', $userpass, 2);
         if ($matches[0] == $username) {
             $crypted = $matches[1];
             // This is about the only attribute we can add
             $attributes = array_merge(array('uid' => array($username)), $this->attributes);
             // Traditional crypt(3)
             if (crypt($password, $crypted) == $crypted) {
                 SimpleSAML\Logger::debug('User ' . $username . ' authenticated successfully');
                 return $attributes;
             }
             // Apache's custom MD5
             if (APR1_MD5::check($crypted, $password)) {
                 SimpleSAML\Logger::debug('User ' . $username . ' authenticated successfully');
                 return $attributes;
             }
             // SHA1 or plain-text
             if (SimpleSAML\Utils\Crypto::pwValid($crypted, $password)) {
                 SimpleSAML\Logger::debug('User ' . $username . ' authenticated successfully');
                 return $attributes;
             }
             throw new SimpleSAML_Error_Error('WRONGUSERPASS');
         }
     }
     throw new SimpleSAML_Error_Error('WRONGUSERPASS');
 }
Exemplo n.º 3
0
 public function finalStep(&$state)
 {
     $requestToken = $state['authtwitter:authdata:requestToken'];
     $parameters = array();
     if (!isset($_REQUEST['oauth_token'])) {
         throw new SimpleSAML_Error_BadRequest("Missing oauth_token parameter.");
     }
     if ($requestToken->key !== (string) $_REQUEST['oauth_token']) {
         throw new SimpleSAML_Error_BadRequest("Invalid oauth_token parameter.");
     }
     if (!isset($_REQUEST['oauth_verifier'])) {
         throw new SimpleSAML_Error_BadRequest("Missing oauth_verifier parameter.");
     }
     $parameters['oauth_verifier'] = (string) $_REQUEST['oauth_verifier'];
     $consumer = new sspmod_oauth_Consumer($this->key, $this->secret);
     SimpleSAML\Logger::debug("oauth: Using this request token [" . $requestToken->key . "] with the secret [" . $requestToken->secret . "]");
     // Replace the request token with an access token
     $accessToken = $consumer->getAccessToken('https://api.twitter.com/oauth/access_token', $requestToken, $parameters);
     SimpleSAML\Logger::debug("Got an access token from the OAuth service provider [" . $accessToken->key . "] with the secret [" . $accessToken->secret . "]");
     $userdata = $consumer->getUserInfo('https://api.twitter.com/1.1/account/verify_credentials.json', $accessToken);
     if (!isset($userdata['id_str']) || !isset($userdata['screen_name'])) {
         throw new SimpleSAML_Error_AuthSource($this->authId, 'Authentication error: id_str and screen_name not set.');
     }
     $attributes = array();
     foreach ($userdata as $key => $value) {
         if (is_string($value)) {
             $attributes['twitter.' . $key] = array((string) $value);
         }
     }
     $attributes['twitter_at_screen_name'] = array('@' . $userdata['screen_name']);
     $attributes['twitter_screen_n_realm'] = array($userdata['screen_name'] . '@twitter.com');
     $attributes['twitter_targetedID'] = array('http://twitter.com!' . $userdata['id_str']);
     $state['Attributes'] = $attributes;
 }
Exemplo n.º 4
0
 /**
  * Apply filter to add groups attribute.
  *
  * @param array &$request  The current request
  */
 public function process(&$request)
 {
     assert('is_array($request)');
     assert('array_key_exists("Attributes", $request)');
     $groups = array();
     $attributes =& $request['Attributes'];
     $realm = self::getRealm($attributes);
     if ($realm !== NULL) {
         $groups[] = 'realm-' . $realm;
     }
     foreach ($this->generateGroupsFrom as $name) {
         if (!array_key_exists($name, $attributes)) {
             SimpleSAML\Logger::debug('GenerateGroups - attribute \'' . $name . '\' not found.');
             /* Attribute not present. */
             continue;
         }
         foreach ($attributes[$name] as $value) {
             $value = self::escapeIllegalChars($value);
             $groups[] = $name . '-' . $value;
             if ($realm !== NULL) {
                 $groups[] = $name . '-' . $realm . '-' . $value;
             }
         }
     }
     if (count($groups) > 0) {
         $attributes['groups'] = $groups;
     }
 }
Exemplo n.º 5
0
 /**
  * Clean the logout table of expired entries.
  *
  * @param SimpleSAML_Store_SQL $store  The datastore.
  */
 private static function cleanLogoutStore(SimpleSAML_Store_SQL $store)
 {
     SimpleSAML\Logger::debug('saml.LogoutStore: Cleaning logout store.');
     $query = 'DELETE FROM ' . $store->prefix . '_saml_LogoutStore WHERE _expire < :now';
     $params = array('now' => gmdate('Y-m-d H:i:s'));
     $query = $store->pdo->prepare($query);
     $query->execute($params);
 }
Exemplo n.º 6
0
 /**
  * Check that the user has access to the statistics.
  *
  * If the user doesn't have access, send the user to the login page.
  */
 public static function checkAccess(SimpleSAML_Configuration $statconfig)
 {
     $protected = $statconfig->getBoolean('protected', FALSE);
     $authsource = $statconfig->getString('auth', NULL);
     $allowedusers = $statconfig->getValue('allowedUsers', NULL);
     $useridattr = $statconfig->getString('useridattr', 'eduPersonPrincipalName');
     $acl = $statconfig->getValue('acl', NULL);
     if ($acl !== NULL && !is_string($acl) && !is_array($acl)) {
         throw new SimpleSAML_Error_Exception('Invalid value for \'acl\'-option. Should be an array or a string.');
     }
     if (!$protected) {
         return;
     }
     if (SimpleSAML\Utils\Auth::isAdmin()) {
         // User logged in as admin. OK.
         SimpleSAML\Logger::debug('Statistics auth - logged in as admin, access granted');
         return;
     }
     if (!isset($authsource)) {
         // If authsource is not defined, init admin login.
         SimpleSAML\Utils\Auth::requireAdmin();
     }
     // We are using an authsource for login.
     $as = new SimpleSAML_Auth_Simple($authsource);
     $as->requireAuth();
     // User logged in with auth source.
     SimpleSAML\Logger::debug('Statistics auth - valid login with auth source [' . $authsource . ']');
     // Retrieving attributes
     $attributes = $as->getAttributes();
     if (!empty($allowedusers)) {
         // Check if userid exists
         if (!isset($attributes[$useridattr][0])) {
             throw new Exception('User ID is missing');
         }
         // Check if userid is allowed access..
         if (in_array($attributes[$useridattr][0], $allowedusers)) {
             SimpleSAML\Logger::debug('Statistics auth - User granted access by user ID [' . $attributes[$useridattr][0] . ']');
             return;
         }
         SimpleSAML\Logger::debug('Statistics auth - User denied access by user ID [' . $attributes[$useridattr][0] . ']');
     } else {
         SimpleSAML\Logger::debug('Statistics auth - no allowedUsers list.');
     }
     if (!is_null($acl)) {
         $acl = new sspmod_core_ACL($acl);
         if ($acl->allows($attributes)) {
             SimpleSAML\Logger::debug('Statistics auth - allowed access by ACL.');
             return;
         }
         SimpleSAML\Logger::debug('Statistics auth - denied access by ACL.');
     } else {
         SimpleSAML\Logger::debug('Statistics auth - no ACL configured.');
     }
     throw new SimpleSAML_Error_Exception('Access denied to the current user.');
 }
 /**
  * Returns the name of the transform class based on a given URI
  *
  * @throws Exception
  * @param string $uri The transform URI
  * @return string The transform implementation class name
  */
 protected function _findClassbyURI($uri)
 {
     switch ($uri) {
         case 'http://www.w3.org/2000/09/xmldsig#enveloped-signature':
             return 'Zend_InfoCard_Xml_Security_Transform_EnvelopedSignature';
         case 'http://www.w3.org/2001/10/xml-exc-c14n#':
             return 'Zend_InfoCard_Xml_Security_Transform_XmlExcC14N';
         default:
             SimpleSAML\Logger::debug("Unknown or Unsupported Transformation Requested");
     }
 }
 /**
  * Transforms the XML Document according to the EnvelopedSignature Transform
  *
  * @throws Exception
  * @param string $strXMLData The input XML data
  * @return string the transformed XML data
  */
 public function transform($strXMLData)
 {
     $sxe = simplexml_load_string($strXMLData);
     $sxe->registerXPathNamespace('ds', 'http://www.w3.org/2000/09/xmldsig#');
     list($signature) = $sxe->xpath("//ds:Signature");
     if (!isset($signature)) {
         SimpleSAML\Logger::debug("Unable to locate Signature Block for EnvelopedSignature Transform");
     }
     $transformed_xml = str_replace($signature->asXML(), "", $sxe->asXML());
     return $transformed_xml;
 }
Exemplo n.º 9
0
 /**
  * Save consent.
  *
  * Called when the user asks for the consent to be saved. If consent information for the given user and destination
  * already exists, it should be overwritten.
  *
  * @param string $userId        The hash identifying the user at an IdP.
  * @param string $destinationId A string which identifies the destination.
  * @param string $attributeSet  A hash which identifies the attributes.
  *
  * @return void
  */
 public function saveConsent($userId, $destinationId, $attributeSet)
 {
     assert('is_string($userId)');
     assert('is_string($destinationId)');
     assert('is_string($attributeSet)');
     $name = self::_getCookieName($userId, $destinationId);
     $value = $userId . ':' . $attributeSet . ':' . $destinationId;
     SimpleSAML\Logger::debug('Consent cookie - Set [' . $value . ']');
     $value = self::_sign($value);
     $this->_setConsentCookie($name, $value);
 }
Exemplo n.º 10
0
 /**
  * Filter out YubiKey 'otp' attribute and replace it with
  * a 'yubiPrefix' attribute that leaves out the dynamic part.
  *
  * @param array &$state  The state we should update.
  */
 public function process(&$state)
 {
     assert('is_array($state)');
     assert('array_key_exists("Attributes", $state)');
     $attributes = $state['Attributes'];
     SimpleSAML\Logger::debug('OTP2YubiPrefix: enter with attributes: ' . implode(',', array_keys($attributes)));
     $otps = $attributes['otp'];
     $otp = $otps['0'];
     $token_size = 32;
     $identity = substr($otp, 0, strlen($otp) - $token_size);
     $attributes['yubiPrefix'] = array($identity);
     SimpleSAML\Logger::info('OTP2YubiPrefix: otp: ' . $otp . ' identity: ' . $identity . ' (otp keys: ' . implode(',', array_keys($otps)) . ')');
     unset($attributes['otp']);
     SimpleSAML\Logger::debug('OTP2YubiPrefix: leaving with attributes: ' . implode(',', array_keys($attributes)));
 }
 /**
  * Transform the input XML based on C14n XML Exclusive Canonicalization rules
  *
  * @throws Exception
  * @param string $strXMLData The input XML
  * @return string The output XML
  */
 public function transform($strXMLData)
 {
     $dom = new DOMDocument();
     $dom->loadXML($strXMLData);
     if ($strXMLData == NULL) {
         SimpleSAML\Logger::debug("NOXML: " . $dom->saveXML());
     } else {
         SimpleSAML\Logger::debug("XMLcan: " . $dom->saveXML());
     }
     if (method_exists($dom, 'C14N')) {
         return $dom->C14N(true, false);
     }
     SimpleSAML\Logger::debug("This transform requires the C14N() method to exist in the DOM extension");
     throw new Exception('This transform requires the C14N() method to exist in the DOM extension');
 }
Exemplo n.º 12
0
 /**
  * Attempt to log in using the given username and password.
  *
  * On a successful login, this function should return the users attributes. On failure,
  * it should throw an exception. If the error was caused by the user entering the wrong
  * username OR password, a SimpleSAML_Error_Error('WRONGUSERPASS') should be thrown.
  *
  * The username is UTF-8 encoded, and the hash is base64 encoded.
  *
  * @param string $username  The username the user wrote.
  * @param string $password  The password the user wrote.
  * @return array  Associative array with the users attributes.
  */
 protected function login($username, $password)
 {
     assert('is_string($username)');
     assert('is_string($password)');
     foreach ($this->users as $userpass => $attrs) {
         $matches = explode(':', $userpass, 2);
         if ($matches[0] === $username) {
             if (SimpleSAML\Utils\Crypto::pwValid($matches[1], $password)) {
                 return $this->users[$userpass];
             } else {
                 SimpleSAML\Logger::debug('Incorrect password "' . $password . '" for user ' . $username);
             }
         }
     }
     throw new SimpleSAML_Error_Error('WRONGUSERPASS');
 }
 public static function handleLogin($authStateId, $xmlToken)
 {
     assert('is_string($authStateId)');
     $config = SimpleSAML_Configuration::getInstance();
     $autoconfig = $config->copyFromBase('logininfocard', 'config-login-infocard.php');
     $idp_key = $autoconfig->getValue('idp_key');
     $idp_pass = $autoconfig->getValue('idp_key_pass', NULL);
     $sts_crt = $autoconfig->getValue('sts_crt');
     $Infocard = $autoconfig->getValue('InfoCard');
     $infocard = new sspmod_InfoCard_RP_InfoCard();
     $infocard->addIDPKey($idp_key, $idp_pass);
     $infocard->addSTSCertificate($sts_crt);
     if (!$xmlToken) {
         SimpleSAML\Logger::debug("XMLtoken: " . $xmlToken);
     } else {
         SimpleSAML\Logger::debug("NOXMLtoken: " . $xmlToken);
     }
     $claims = $infocard->process($xmlToken);
     if ($claims->isValid()) {
         $attributes = array();
         foreach ($Infocard['requiredClaims'] as $claim => $data) {
             $attributes[$claim] = array($claims->{$claim});
         }
         foreach ($Infocard['optionalClaims'] as $claim => $data) {
             $attributes[$claim] = array($claims->{$claim});
         }
         // Retrieve the authentication state
         $state = SimpleSAML_Auth_State::loadState($authStateId, self::STAGEID);
         // Find authentication source
         assert('array_key_exists(self::AUTHID, $state)');
         $source = SimpleSAML_Auth_Source::getById($state[self::AUTHID]);
         if ($source === NULL) {
             throw new Exception('Could not find authentication source with id ' . $state[self::AUTHID]);
         }
         $state['Attributes'] = $attributes;
         unset($infocard);
         unset($claims);
         SimpleSAML_Auth_Source::completeAuth($state);
     } else {
         unset($infocard);
         unset($claims);
         return 'wrong_IC';
     }
 }
Exemplo n.º 14
0
 /**
  * Initialize an authentication processing chain for the given service provider
  * and identity provider.
  *
  * @param array $idpMetadata  The metadata for the IdP.
  * @param array $spMetadata  The metadata for the SP.
  */
 public function __construct($idpMetadata, $spMetadata, $mode = 'idp')
 {
     assert('is_array($idpMetadata)');
     assert('is_array($spMetadata)');
     $this->filters = array();
     $config = SimpleSAML_Configuration::getInstance();
     $configauthproc = $config->getArray('authproc.' . $mode, NULL);
     if (!empty($configauthproc)) {
         $configfilters = self::parseFilterList($configauthproc);
         self::addFilters($this->filters, $configfilters);
     }
     if (array_key_exists('authproc', $idpMetadata)) {
         $idpFilters = self::parseFilterList($idpMetadata['authproc']);
         self::addFilters($this->filters, $idpFilters);
     }
     if (array_key_exists('authproc', $spMetadata)) {
         $spFilters = self::parseFilterList($spMetadata['authproc']);
         self::addFilters($this->filters, $spFilters);
     }
     SimpleSAML\Logger::debug('Filter config for ' . $idpMetadata['entityid'] . '->' . $spMetadata['entityid'] . ': ' . str_replace("\n", '', var_export($this->filters, TRUE)));
 }
Exemplo n.º 15
0
 /**
  * Apply filter to add or replace attributes.
  *
  * Add or replace existing attributes with the configured values.
  *
  * @param array &$request  The current request
  */
 public function process(&$request)
 {
     assert('is_array($request)');
     assert('array_key_exists("Attributes", $request)');
     $attributes =& $request['Attributes'];
     $attrlang = NULL;
     if (array_key_exists($this->langattr, $attributes)) {
         $attrlang = $attributes[$this->langattr][0];
     }
     $lang = SimpleSAML\Locale\Language::getLanguageCookie();
     if (isset($attrlang)) {
         SimpleSAML\Logger::debug('LanguageAdaptor: Language in attribute was set [' . $attrlang . ']');
     }
     if (isset($lang)) {
         SimpleSAML\Logger::debug('LanguageAdaptor: Language in session   was set [' . $lang . ']');
     }
     if (isset($attrlang) && !isset($lang)) {
         // Language set in attribute but not in cookie - update cookie
         SimpleSAML\Locale\Language::setLanguageCookie($attrlang);
     } elseif (!isset($attrlang) && isset($lang)) {
         // Language set in cookie, but not in attribute. Update attribute
         $request['Attributes'][$this->langattr] = array($lang);
     }
 }
Exemplo n.º 16
0
 /**
  * Getter for the LDAP connection object. Created this getter
  * rather than setting in the constructor to avoid unnecessarily
  * connecting to LDAP when it might not be needed.
  *
  * @return sspmod_ldap_LdapConnection
  */
 protected function getLdap()
 {
     // Check if already connected
     if ($this->ldap) {
         return $this->ldap;
     }
     // Get the connection specific options
     $hostname = $this->config->getString('ldap.hostname');
     $port = $this->config->getInteger('ldap.port', 389);
     $enable_tls = $this->config->getBoolean('ldap.enable_tls', false);
     $debug = $this->config->getBoolean('ldap.debug', false);
     $timeout = $this->config->getInteger('ldap.timeout', 0);
     $username = $this->config->getString('ldap.username', null);
     $password = $this->config->getString('ldap.password', null);
     // Log the LDAP connection
     SimpleSAML\Logger::debug($this->title . 'Connecting to LDAP server;' . ' Hostname: ' . $hostname . ' Port: ' . $port . ' Enable TLS: ' . ($enable_tls ? 'Yes' : 'No') . ' Debug: ' . ($debug ? 'Yes' : 'No') . ' Timeout: ' . $timeout . ' Username: '******' Password: '******'*', strlen($password)));
     // Connect to the LDAP server to be queried during processing
     $this->ldap = new SimpleSAML_Auth_LDAP($hostname, $enable_tls, $debug, $timeout, $port);
     $this->ldap->bind($username, $password);
     // All done
     return $this->ldap;
 }
Exemplo n.º 17
0
 /**
  * This function checks if this EntityDescriptor was signed with a certificate with the
  * given fingerprint.
  *
  * @param string $fingerprint Fingerprint of the certificate which should have been used to sign this
  *                      EntityDescriptor.
  *
  * @return boolean True if it was signed with the certificate with the given fingerprint, false otherwise.
  */
 public function validateFingerprint($fingerprint)
 {
     assert('is_string($fingerprint)');
     $fingerprint = strtolower(str_replace(":", "", $fingerprint));
     $candidates = array();
     foreach ($this->validators as $validator) {
         foreach ($validator->getValidatingCertificates() as $cert) {
             $fp = strtolower(sha1(base64_decode($cert)));
             $candidates[] = $fp;
             if ($fp === $fingerprint) {
                 return true;
             }
         }
     }
     SimpleSAML\Logger::debug('Fingerprint was [' . $fingerprint . '] not one of [' . join(', ', $candidates) . ']');
     return false;
 }
 /**
  * Retrieve the metadata file.
  *
  * This function will check its cached copy, to see whether it can be used.
  *
  * @return SAML2_XML_md_EntityDescriptor|SAML2_XML_md_EntitiesDescriptor|NULL  The downloaded metadata.
  */
 public function getMetadata()
 {
     if ($this->metadata !== NULL) {
         /* We have already downloaded the metdata. */
         return $this->metadata;
     }
     if (!$this->aggregator->isCacheValid($this->cacheId, $this->cacheTag)) {
         $this->updateCache();
         if ($this->metadata !== NULL) {
             return $this->metadata;
         }
         /* We were unable to update the cache - use cached metadata. */
     }
     $cacheFile = $this->aggregator->getCacheFile($this->cacheId);
     if (!file_exists($cacheFile)) {
         SimpleSAML\Logger::error($this->logLoc . 'No cached metadata available.');
         return NULL;
     }
     SimpleSAML\Logger::debug($this->logLoc . 'Using cached metadata from ' . var_export($cacheFile, TRUE));
     $metadata = file_get_contents($cacheFile);
     if ($metadata !== NULL) {
         $this->metadata = unserialize($metadata);
         return $this->metadata;
     }
     return NULL;
 }
Exemplo n.º 19
0
 /**
  * Send a logout response.
  *
  * @param SimpleSAML_IdP $idp  The IdP we are sending a logout request from.
  * @param array &$state  The logout state array.
  */
 public static function sendLogoutResponse(SimpleSAML_IdP $idp, array $state)
 {
     assert('isset($state["saml:SPEntityId"])');
     assert('isset($state["saml:RequestId"])');
     assert('array_key_exists("saml:RelayState", $state)');
     // Can be NULL.
     $spEntityId = $state['saml:SPEntityId'];
     $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
     $idpMetadata = $idp->getConfig();
     $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote');
     $lr = sspmod_saml_Message::buildLogoutResponse($idpMetadata, $spMetadata);
     $lr->setInResponseTo($state['saml:RequestId']);
     $lr->setRelayState($state['saml:RelayState']);
     if (isset($state['core:Failed']) && $state['core:Failed']) {
         $partial = TRUE;
         $lr->setStatus(array('Code' => SAML2_Const::STATUS_SUCCESS, 'SubCode' => SAML2_Const::STATUS_PARTIAL_LOGOUT));
         SimpleSAML\Logger::info('Sending logout response for partial logout to SP ' . var_export($spEntityId, TRUE));
     } else {
         $partial = FALSE;
         SimpleSAML\Logger::debug('Sending logout response to SP ' . var_export($spEntityId, TRUE));
     }
     SimpleSAML_Stats::log('saml:idp:LogoutResponse:sent', array('spEntityID' => $spEntityId, 'idpEntityID' => $idpMetadata->getString('entityid'), 'partial' => $partial));
     $dst = $spMetadata->getEndpointPrioritizedByBinding('SingleLogoutService', array(SAML2_Const::BINDING_HTTP_REDIRECT, SAML2_Const::BINDING_HTTP_POST));
     $binding = SAML2_Binding::getBinding($dst['Binding']);
     if (isset($dst['ResponseLocation'])) {
         $dst = $dst['ResponseLocation'];
     } else {
         $dst = $dst['Location'];
     }
     $lr->setDestination($dst);
     $binding->send($lr);
 }
Exemplo n.º 20
0
 /**
  * Send a SAML2 SSO request to an IdP.
  *
  * @param SimpleSAML_Configuration $idpMetadata  The metadata of the IdP.
  * @param array $state  The state array for the current authentication.
  */
 private function startSSO2(SimpleSAML_Configuration $idpMetadata, array $state)
 {
     if (isset($state['saml:ProxyCount']) && $state['saml:ProxyCount'] < 0) {
         SimpleSAML_Auth_State::throwException($state, new SimpleSAML_Error_ProxyCountExceeded("ProxyCountExceeded"));
     }
     $ar = sspmod_saml_Message::buildAuthnRequest($this->metadata, $idpMetadata);
     $ar->setAssertionConsumerServiceURL(SimpleSAML\Module::getModuleURL('saml/sp/saml2-acs.php/' . $this->authId));
     if (isset($state['SimpleSAML_Auth_Source.ReturnURL'])) {
         $ar->setRelayState($state['SimpleSAML_Auth_Source.ReturnURL']);
     }
     if (isset($state['saml:AuthnContextClassRef'])) {
         $accr = SimpleSAML\Utils\Arrays::arrayize($state['saml:AuthnContextClassRef']);
         $ar->setRequestedAuthnContext(array('AuthnContextClassRef' => $accr));
     }
     if (isset($state['ForceAuthn'])) {
         $ar->setForceAuthn((bool) $state['ForceAuthn']);
     }
     if (isset($state['isPassive'])) {
         $ar->setIsPassive((bool) $state['isPassive']);
     }
     if (isset($state['saml:NameIDPolicy'])) {
         if (is_string($state['saml:NameIDPolicy'])) {
             $policy = array('Format' => (string) $state['saml:NameIDPolicy'], 'AllowCreate' => TRUE);
         } elseif (is_array($state['saml:NameIDPolicy'])) {
             $policy = $state['saml:NameIDPolicy'];
         } else {
             throw new SimpleSAML_Error_Exception('Invalid value of $state[\'saml:NameIDPolicy\'].');
         }
         $ar->setNameIdPolicy($policy);
     }
     if (isset($state['saml:IDPList'])) {
         $IDPList = $state['saml:IDPList'];
     } else {
         $IDPList = array();
     }
     $ar->setIDPList(array_unique(array_merge($this->metadata->getArray('IDPList', array()), $idpMetadata->getArray('IDPList', array()), (array) $IDPList)));
     if (isset($state['saml:ProxyCount']) && $state['saml:ProxyCount'] !== null) {
         $ar->setProxyCount($state['saml:ProxyCount']);
     } elseif ($idpMetadata->getInteger('ProxyCount', null) !== null) {
         $ar->setProxyCount($idpMetadata->getInteger('ProxyCount', null));
     } elseif ($this->metadata->getInteger('ProxyCount', null) !== null) {
         $ar->setProxyCount($this->metadata->getInteger('ProxyCount', null));
     }
     $requesterID = array();
     if (isset($state['saml:RequesterID'])) {
         $requesterID = $state['saml:RequesterID'];
     }
     if (isset($state['core:SP'])) {
         $requesterID[] = $state['core:SP'];
     }
     $ar->setRequesterID($requesterID);
     if (isset($state['saml:Extensions'])) {
         $ar->setExtensions($state['saml:Extensions']);
     }
     // save IdP entity ID as part of the state
     $state['ExpectedIssuer'] = $idpMetadata->getString('entityid');
     $id = SimpleSAML_Auth_State::saveState($state, 'saml:sp:sso', TRUE);
     $ar->setId($id);
     SimpleSAML\Logger::debug('Sending SAML 2 AuthnRequest to ' . var_export($idpMetadata->getString('entityid'), TRUE));
     /* Select appropriate SSO endpoint */
     if ($ar->getProtocolBinding() === SAML2_Const::BINDING_HOK_SSO) {
         $dst = $idpMetadata->getDefaultEndpoint('SingleSignOnService', array(SAML2_Const::BINDING_HOK_SSO));
     } else {
         $dst = $idpMetadata->getDefaultEndpoint('SingleSignOnService', array(SAML2_Const::BINDING_HTTP_REDIRECT, SAML2_Const::BINDING_HTTP_POST));
     }
     $ar->setDestination($dst['Location']);
     $b = SAML2_Binding::getBinding($dst['Binding']);
     $this->sendSAML2AuthnRequest($state, $b, $ar);
     assert('FALSE');
 }
 /**
  * Save a metadata entry.
  *
  * @param string $entityId The entityId of the metadata entry.
  * @param string $set The metadata set this metadata entry belongs to.
  * @param array $metadata The metadata.
  *
  * @return boolean True if successfully saved, false otherwise.
  */
 public function saveMetadata($entityId, $set, $metadata)
 {
     assert('is_string($entityId)');
     assert('is_string($set)');
     assert('is_array($metadata)');
     $filePath = $this->getMetadataPath($entityId, $set);
     $newPath = $filePath . '.new';
     $dir = dirname($filePath);
     if (!is_dir($dir)) {
         SimpleSAML\Logger::info('Creating directory: ' . $dir);
         $res = @mkdir($dir, 0777, true);
         if ($res === false) {
             $error = error_get_last();
             SimpleSAML\Logger::error('Failed to create directory ' . $dir . ': ' . $error['message']);
             return false;
         }
     }
     $data = serialize($metadata);
     SimpleSAML\Logger::debug('Writing: ' . $newPath);
     $res = file_put_contents($newPath, $data);
     if ($res === false) {
         $error = error_get_last();
         SimpleSAML\Logger::error('Error saving file ' . $newPath . ': ' . $error['message']);
         return false;
     }
     $res = rename($newPath, $filePath);
     if ($res === false) {
         $error = error_get_last();
         SimpleSAML\Logger::error('Error renaming ' . $newPath . ' to ' . $filePath . ': ' . $error['message']);
         return false;
     }
     return true;
 }
Exemplo n.º 22
0
    function send()
    {
        if ($this->to == NULL) {
            throw new Exception('EMail field [to] is required and not set.');
        }
        if ($this->subject == NULL) {
            throw new Exception('EMail field [subject] is required and not set.');
        }
        if ($this->body == NULL) {
            throw new Exception('EMail field [body] is required and not set.');
        }
        $random_hash = bin2hex(openssl_random_pseudo_bytes(16));
        if (isset($this->from)) {
            $this->headers[] = 'From: ' . $this->from;
        }
        if (isset($this->replyto)) {
            $this->headers[] = 'Reply-To: ' . $this->replyto;
        }
        $this->headers[] = 'Content-Type: multipart/alternative; boundary="simplesamlphp-' . $random_hash . '"';
        $message = '
--simplesamlphp-' . $random_hash . '
Content-Type: text/plain; charset="utf-8" 
Content-Transfer-Encoding: 8bit

' . strip_tags(html_entity_decode($this->body)) . '

--simplesamlphp-' . $random_hash . '
Content-Type: text/html; charset="utf-8" 
Content-Transfer-Encoding: 8bit

' . $this->getHTML($this->body) . '

--simplesamlphp-' . $random_hash . '--
';
        $headers = implode("\n", $this->headers);
        $mail_sent = @mail($this->to, $this->subject, $message, $headers);
        SimpleSAML\Logger::debug('Email: Sending e-mail to [' . $this->to . '] : ' . ($mail_sent ? 'OK' : 'Failed'));
        if (!$mail_sent) {
            throw new Exception('Error when sending e-mail');
        }
    }
Exemplo n.º 23
0
 /**
  * Post-logout handler for re-authentication.
  *
  * This method will never return.
  *
  * @param SimpleSAML_IdP $idp The IdP we are logging out from.
  * @param array &$state The state array with the state during logout.
  */
 public static function reauthPostLogout(SimpleSAML_IdP $idp, array $state)
 {
     assert('isset($state["saml:sp:AuthId"])');
     SimpleSAML\Logger::debug('Proxy: logout completed.');
     if (isset($state['saml:proxy:reauthLogout:PrevResponder'])) {
         $state['Responder'] = $state['saml:proxy:reauthLogout:PrevResponder'];
     }
     $sp = SimpleSAML_Auth_Source::getById($state['saml:sp:AuthId'], 'sspmod_saml_Auth_Source_SP');
     /** @var sspmod_saml_Auth_Source_SP $authSource */
     SimpleSAML\Logger::debug('Proxy: logging in again.');
     $sp->authenticate($state);
     assert('false');
 }
Exemplo n.º 24
0
 /**
  * Delete all consents.
  *
  * @param string $userId The hash identifying the user at an IdP.
  *
  * @return int Number of consents deleted
  */
 public function deleteAllConsents($userId)
 {
     assert('is_string($userId)');
     $st = $this->_execute('DELETE FROM ' . $this->_table . ' WHERE hashed_user_id = ?', array($userId));
     if ($st === false) {
         return;
     }
     if ($st->rowCount() > 0) {
         SimpleSAML\Logger::debug('consent:Database - Deleted (' . $st->rowCount() . ') consent(s).');
         return $st->rowCount();
     } else {
         SimpleSAML\Logger::warning('consent:Database - Attempted to delete nonexistent consent');
     }
 }
Exemplo n.º 25
0
 /**
  * Is the session representing an authenticated user, and is the session still alive.
  * This function will return false after the user has timed out.
  *
  * @param string $authority The authentication source that the user should be authenticated with.
  *
  * @return true if the user has a valid session, false if not.
  */
 public function isValid($authority)
 {
     assert('is_string($authority)');
     if (!isset($this->authData[$authority])) {
         SimpleSAML\Logger::debug('Session: ' . var_export($authority, true) . ' not valid because we are not authenticated.');
         return false;
     }
     if ($this->authData[$authority]['Expire'] <= time()) {
         SimpleSAML\Logger::debug('Session: ' . var_export($authority, true) . ' not valid because it is expired.');
         return false;
     }
     SimpleSAML\Logger::debug('Session: Valid session found with ' . var_export($authority, true) . '.');
     return true;
 }
 /**
  * Process this filter
  *
  * Logic is largely the same as (and lifted from) sqlauth:sql
  * @param mixed &$request
  * @throws SimpleSAML_Error_Exception
  */
 public function process(&$request)
 {
     assert('is_array($request)');
     assert('array_key_exists("Attributes", $request)');
     assert('array_key_exists("entityid", $request["Destination"])');
     $attributes =& $request['Attributes'];
     if (!array_key_exists($this->attribute, $attributes)) {
         SimpleSAML\Logger::info('AttributeFromSQL: attribute \'' . $this->attribute . '\' not set, declining');
         return;
     }
     $db = $this->connect();
     try {
         $sth = $db->prepare('SELECT attribute,value FROM ' . $this->table . ' WHERE uid=? AND (sp=\'%\' OR sp=?);');
     } catch (PDOException $e) {
         throw new SimpleSAML_Error_Exception('AttributeFromSQL: prepare() failed: ' . $e->getMessage());
     }
     try {
         $res = $sth->execute(array($attributes[$this->attribute][0], $request["Destination"]["entityid"]));
     } catch (PDOException $e) {
         throw new SimpleSAML_Error_Exception('AttributeFromSQL: execute(' . $attributes[$this->attribute][0] . ', ' . $request["Destination"]["entityid"] . ') failed: ' . $e->getMessage());
     }
     try {
         $data = $sth->fetchAll(PDO::FETCH_ASSOC);
     } catch (PDOException $e) {
         throw new SimpleSAML_Error_Exception('AttributeFromSQL: fetchAll() failed: ' . $e->getMessage());
     }
     if (count($data) === 0) {
         SimpleSAML\Logger::info('AttributeFromSQL: no additional attributes for ' . $this->attribute . '=\'' . $attributes[$this->attribute][0] . '\'');
         return;
     }
     /* Extract attributes from the SQL datasource, and then merge them into
      * the existing attribute set. If $replace is set, overwrite any existing
      * attribute of the same name; otherwise add it as a multi-valued attribute
      */
     foreach ($data as $row) {
         if (empty($row['attribute']) || $row['value'] === null) {
             SimpleSAML\Logger::debug('AttributeFromSQL: skipping invalid attribute/value tuple: ' . var_export($row, true));
             continue;
         }
         $name = (string) $row['attribute'];
         $value = (string) $row['value'];
         /* Limit the attribute set returned */
         if ($this->limit !== null && !in_array($name, $this->limit, true)) {
             SimpleSAML\Logger::notice('AttributeFromSQL: skipping unwanted attribute ' . $name . ' [limited to: ' . var_export($this->limit, true) . ']');
             continue;
         }
         if (!array_key_exists($name, $attributes) || $this->replace === true) {
             $attributes[$name] = array();
         }
         if (in_array($value, $attributes[$name], true)) {
             /* Value already exists in attribute. */
             SimpleSAML\Logger::debug('AttributeFromSQL: skipping duplicate attribute/value tuple ' . $name . '=\'' . $value . '\'');
             continue;
         }
         $attributes[$name][] = $value;
     }
 }
Exemplo n.º 27
0
 /**
  * Process an assertion in a response.
  *
  * Will throw an exception if it is invalid.
  *
  * @param SimpleSAML_Configuration $spMetadata  The metadata of the service provider.
  * @param SimpleSAML_Configuration $idpMetadata  The metadata of the identity provider.
  * @param \SAML2\Response $response  The response containing the assertion.
  * @param \SAML2\Assertion|\SAML2\EncryptedAssertion $assertion  The assertion.
  * @param bool $responseSigned  Whether the response is signed.
  * @return \SAML2\Assertion  The assertion, if it is valid.
  */
 private static function processAssertion(SimpleSAML_Configuration $spMetadata, SimpleSAML_Configuration $idpMetadata, \SAML2\Response $response, $assertion, $responseSigned)
 {
     assert('$assertion instanceof \\SAML2\\Assertion || $assertion instanceof \\SAML2\\EncryptedAssertion');
     assert('is_bool($responseSigned)');
     $assertion = self::decryptAssertion($idpMetadata, $spMetadata, $assertion);
     if (!self::checkSign($idpMetadata, $assertion)) {
         if (!$responseSigned) {
             throw new SimpleSAML_Error_Exception('Neither the assertion nor the response was signed.');
         }
     }
     /* At least one valid signature found. */
     $currentURL = \SimpleSAML\Utils\HTTP::getSelfURLNoQuery();
     /* Check various properties of the assertion. */
     $notBefore = $assertion->getNotBefore();
     if ($notBefore !== NULL && $notBefore > time() + 60) {
         throw new SimpleSAML_Error_Exception('Received an assertion that is valid in the future. Check clock synchronization on IdP and SP.');
     }
     $notOnOrAfter = $assertion->getNotOnOrAfter();
     if ($notOnOrAfter !== NULL && $notOnOrAfter <= time() - 60) {
         throw new SimpleSAML_Error_Exception('Received an assertion that has expired. Check clock synchronization on IdP and SP.');
     }
     $sessionNotOnOrAfter = $assertion->getSessionNotOnOrAfter();
     if ($sessionNotOnOrAfter !== NULL && $sessionNotOnOrAfter <= time() - 60) {
         throw new SimpleSAML_Error_Exception('Received an assertion with a session that has expired. Check clock synchronization on IdP and SP.');
     }
     $validAudiences = $assertion->getValidAudiences();
     if ($validAudiences !== NULL) {
         $spEntityId = $spMetadata->getString('entityid');
         if (!in_array($spEntityId, $validAudiences, TRUE)) {
             $candidates = '[' . implode('], [', $validAudiences) . ']';
             throw new SimpleSAML_Error_Exception('This SP [' . $spEntityId . ']  is not a valid audience for the assertion. Candidates were: ' . $candidates);
         }
     }
     $found = FALSE;
     $lastError = 'No SubjectConfirmation element in Subject.';
     $validSCMethods = array(\SAML2\Constants::CM_BEARER, \SAML2\Constants::CM_HOK, \SAML2\Constants::CM_VOUCHES);
     foreach ($assertion->getSubjectConfirmation() as $sc) {
         if (!in_array($sc->Method, $validSCMethods)) {
             $lastError = 'Invalid Method on SubjectConfirmation: ' . var_export($sc->Method, TRUE);
             continue;
         }
         /* Is SSO with HoK enabled? IdP remote metadata overwrites SP metadata configuration. */
         $hok = $idpMetadata->getBoolean('saml20.hok.assertion', NULL);
         if ($hok === NULL) {
             $hok = $spMetadata->getBoolean('saml20.hok.assertion', FALSE);
         }
         if ($sc->Method === \SAML2\Constants::CM_BEARER && $hok) {
             $lastError = 'Bearer SubjectConfirmation received, but Holder-of-Key SubjectConfirmation needed';
             continue;
         }
         if ($sc->Method === \SAML2\Constants::CM_HOK && !$hok) {
             $lastError = 'Holder-of-Key SubjectConfirmation received, but the Holder-of-Key profile is not enabled.';
             continue;
         }
         $scd = $sc->SubjectConfirmationData;
         if ($sc->Method === \SAML2\Constants::CM_HOK) {
             /* Check HoK Assertion */
             if (\SimpleSAML\Utils\HTTP::isHTTPS() === FALSE) {
                 $lastError = 'No HTTPS connection, but required for Holder-of-Key SSO';
                 continue;
             }
             if (isset($_SERVER['SSL_CLIENT_CERT']) && empty($_SERVER['SSL_CLIENT_CERT'])) {
                 $lastError = 'No client certificate provided during TLS Handshake with SP';
                 continue;
             }
             /* Extract certificate data (if this is a certificate). */
             $clientCert = $_SERVER['SSL_CLIENT_CERT'];
             $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
             if (!preg_match($pattern, $clientCert, $matches)) {
                 $lastError = 'Error while looking for client certificate during TLS handshake with SP, the client certificate does not ' . 'have the expected structure';
                 continue;
             }
             /* We have a valid client certificate from the browser. */
             $clientCert = str_replace(array("\r", "\n", " "), '', $matches[1]);
             foreach ($scd->info as $thing) {
                 if ($thing instanceof \SAML2\XML\ds\KeyInfo) {
                     $keyInfo[] = $thing;
                 }
             }
             if (count($keyInfo) != 1) {
                 $lastError = 'Error validating Holder-of-Key assertion: Only one <ds:KeyInfo> element in <SubjectConfirmationData> allowed';
                 continue;
             }
             foreach ($keyInfo[0]->info as $thing) {
                 if ($thing instanceof \SAML2\XML\ds\X509Data) {
                     $x509data[] = $thing;
                 }
             }
             if (count($x509data) != 1) {
                 $lastError = 'Error validating Holder-of-Key assertion: Only one <ds:X509Data> element in <ds:KeyInfo> within <SubjectConfirmationData> allowed';
                 continue;
             }
             foreach ($x509data[0]->data as $thing) {
                 if ($thing instanceof \SAML2\XML\ds\X509Certificate) {
                     $x509cert[] = $thing;
                 }
             }
             if (count($x509cert) != 1) {
                 $lastError = 'Error validating Holder-of-Key assertion: Only one <ds:X509Certificate> element in <ds:X509Data> within <SubjectConfirmationData> allowed';
                 continue;
             }
             $HoKCertificate = $x509cert[0]->certificate;
             if ($HoKCertificate !== $clientCert) {
                 $lastError = 'Provided client certificate does not match the certificate bound to the Holder-of-Key assertion';
                 continue;
             }
         }
         if ($scd->NotBefore && $scd->NotBefore > time() + 60) {
             $lastError = 'NotBefore in SubjectConfirmationData is in the future: ' . $scd->NotBefore;
             continue;
         }
         if ($scd->NotOnOrAfter && $scd->NotOnOrAfter <= time() - 60) {
             $lastError = 'NotOnOrAfter in SubjectConfirmationData is in the past: ' . $scd->NotOnOrAfter;
             continue;
         }
         if ($scd->Recipient !== NULL && $scd->Recipient !== $currentURL) {
             $lastError = 'Recipient in SubjectConfirmationData does not match the current URL. Recipient is ' . var_export($scd->Recipient, TRUE) . ', current URL is ' . var_export($currentURL, TRUE) . '.';
             continue;
         }
         if ($scd->InResponseTo !== NULL && $response->getInResponseTo() !== NULL && $scd->InResponseTo !== $response->getInResponseTo()) {
             $lastError = 'InResponseTo in SubjectConfirmationData does not match the Response. Response has ' . var_export($response->getInResponseTo(), TRUE) . ', SubjectConfirmationData has ' . var_export($scd->InResponseTo, TRUE) . '.';
             continue;
         }
         $found = TRUE;
         break;
     }
     if (!$found) {
         throw new SimpleSAML_Error_Exception('Error validating SubjectConfirmation in Assertion: ' . $lastError);
     }
     /* As far as we can tell, the assertion is valid. */
     /* Maybe we need to base64 decode the attributes in the assertion? */
     if ($idpMetadata->getBoolean('base64attributes', FALSE)) {
         $attributes = $assertion->getAttributes();
         $newAttributes = array();
         foreach ($attributes as $name => $values) {
             $newAttributes[$name] = array();
             foreach ($values as $value) {
                 foreach (explode('_', $value) as $v) {
                     $newAttributes[$name][] = base64_decode($v);
                 }
             }
         }
         $assertion->setAttributes($newAttributes);
     }
     /* Decrypt the NameID element if it is encrypted. */
     if ($assertion->isNameIdEncrypted()) {
         try {
             $keys = self::getDecryptionKeys($idpMetadata, $spMetadata);
         } catch (Exception $e) {
             throw new SimpleSAML_Error_Exception('Error decrypting NameID: ' . $e->getMessage());
         }
         $blacklist = self::getBlacklistedAlgorithms($idpMetadata, $spMetadata);
         $lastException = NULL;
         foreach ($keys as $i => $key) {
             try {
                 $assertion->decryptNameId($key, $blacklist);
                 SimpleSAML\Logger::debug('Decryption with key #' . $i . ' succeeded.');
                 $lastException = NULL;
                 break;
             } catch (Exception $e) {
                 SimpleSAML\Logger::debug('Decryption with key #' . $i . ' failed with exception: ' . $e->getMessage());
                 $lastException = $e;
             }
         }
         if ($lastException !== NULL) {
             throw $lastException;
         }
     }
     return $assertion;
 }
Exemplo n.º 28
0
 /**
  * Delete a key-value pair from the memcache servers.
  *
  * @param string $key The key we should delete.
  */
 public static function delete($key)
 {
     assert('is_string($key)');
     SimpleSAML\Logger::debug("deleting key {$key} from memcache");
     // store this object to all groups of memcache servers
     foreach (self::getMemcacheServers() as $server) {
         $server->delete($key);
     }
 }
Exemplo n.º 29
0
 public function finalStep(&$state)
 {
     $requestToken = $state['authlinkedin:requestToken'];
     $consumer = new sspmod_oauth_Consumer($this->key, $this->secret);
     SimpleSAML\Logger::debug("oauth: Using this request token [" . $requestToken->key . "] with the secret [" . $requestToken->secret . "]");
     // Replace the request token with an access token (via GET method)
     $accessToken = $consumer->getAccessToken('https://api.linkedin.com/uas/oauth/accessToken', $requestToken, array('oauth_verifier' => $state['authlinkedin:oauth_verifier']));
     SimpleSAML\Logger::debug("Got an access token from the OAuth service provider [" . $accessToken->key . "] with the secret [" . $accessToken->secret . "]");
     // TODO: configure attributes (http://developer.linkedin.com/docs/DOC-1061) from config? Limited options via LinkedIn
     $userdata = $consumer->getUserInfo('https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,summary,specialties,picture-url)', $accessToken, array('http' => array('header' => 'x-li-format: json')));
     $attributes = array();
     foreach ($userdata as $key => $value) {
         if (is_string($value)) {
             $attributes['linkedin.' . $key] = array((string) $value);
         }
     }
     // TODO: pass accessToken: key, secret + expiry as attributes?
     if (array_key_exists('id', $userdata)) {
         $attributes['linkedin_targetedID'] = array('http://linkedin.com!' . $userdata['id']);
         $attributes['linkedin_user'] = array($userdata['id'] . '@linkedin.com');
     }
     SimpleSAML\Logger::debug('LinkedIn Returned Attributes: ' . implode(", ", array_keys($attributes)));
     $state['Attributes'] = $attributes;
 }
Exemplo n.º 30
0
}
$idpMetadata = $metadata->getMetadataConfig($idpEntityId, 'saml20-idp-hosted');
$spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote');
// The endpoint we should deliver the message to
$endpoint = $spMetadata->getString('testAttributeEndpoint');
// The attributes we will return
$attributes = array('name' => array('value1', 'value2', 'value3'), 'test' => array('test'));
/* The name format of the attributes. */
$attributeNameFormat = SAML2_Const::NAMEFORMAT_UNSPECIFIED;
/* Determine which attributes we will return. */
$returnAttributes = array_keys($query->getAttributes());
if (count($returnAttributes) === 0) {
    SimpleSAML\Logger::debug('No attributes requested - return all attributes.');
    $returnAttributes = $attributes;
} elseif ($query->getAttributeNameFormat() !== $attributeNameFormat) {
    SimpleSAML\Logger::debug('Requested attributes with wrong NameFormat - no attributes returned.');
    $returnAttributes = array();
} else {
    foreach ($returnAttributes as $name => $values) {
        if (!array_key_exists($name, $attributes)) {
            /* We don't have this attribute. */
            unset($returnAttributes[$name]);
            continue;
        }
        if (count($values) === 0) {
            /* Return all attributes. */
            $returnAttributes[$name] = $attributes[$name];
            continue;
        }
        /* Filter which attribute values we should return. */
        $returnAttributes[$name] = array_intersect($values, $attributes[$name]);