id = $id; $this->logLoc = 'aggregator2:' . $this->id . ': '; $this->cronTag = $config->getString('cron.tag', NULL); $this->cacheDirectory = $config->getString('cache.directory', NULL); if ($this->cacheDirectory !== NULL) { $this->cacheDirectory = SimpleSAML_Utilities::resolvePath($this->cacheDirectory); } $this->cacheGenerated = $config->getInteger('cache.generated', NULL); if ($this->cacheGenerated !== NULL) { $this->cacheId = sha1($this->id); $this->cacheTag = sha1(serialize($config)); } $this->validLength = $config->getInteger('valid.length', 7*24*60*60); $globalConfig = SimpleSAML_Configuration::getInstance(); $certDir = $globalConfig->getPathValue('certdir', 'cert/'); $signKey = $config->getString('sign.privatekey', NULL); if ($signKey !== NULL) { $signKey = SimpleSAML_Utilities::resolvePath($signKey, $certDir); $this->signKey = @file_get_contents($signKey); if ($this->signKey === NULL) { throw new SimpleSAML_Error_Exception('Unable to load private key from ' . var_export($signKey, TRUE)); } } $this->signKeyPass = $config->getString('sign.privatekey_pass', NULL); $signCert = $config->getString('sign.certificate', NULL); if ($signCert !== NULL) { $signCert = SimpleSAML_Utilities::resolvePath($signCert, $certDir); $this->signCert = @file_get_contents($signCert); if ($this->signCert === NULL) { throw new SimpleSAML_Error_Exception('Unable to load certificate file from ' . var_export($signCert, TRUE)); } } $this->sslCAFile = $config->getString('ssl.cafile', NULL); $this->regInfo = $config->getArray('RegistrationInfo', NULL); $this->initSources($config->getConfigList('sources')); } /** * Populate the sources array. * * This is called from the constructor, and can be overridden in subclasses. * * @param array $sources The sources as an array of SimpleSAML_Configuration objects. */ protected function initSources(array $sources) { foreach ($sources as $source) { $this->sources[] = new sspmod_aggregator2_EntitySource($this, $source); } } /** * Return an instance of the aggregator with the given id. * * @param string $id The id of the aggregator. */ public static function getAggregator($id) { assert('is_string($id)'); $config = SimpleSAML_Configuration::getConfig('module_aggregator2.php'); return new sspmod_aggregator2_Aggregator($id, $config->getConfigItem($id)); } /** * Retrieve the ID of the aggregator. * * @return string The ID of this aggregator. */ public function getId() { return $this->id; } /** * Add an item to the cache. * * @param string $id The identifier of this data. * @param string $data The data. * @param int $expires The timestamp the data expires. * @param string|NULL $tag An extra tag that can be used to verify the validity of the cached data. */ public function addCacheItem($id, $data, $expires, $tag = NULL) { assert('is_string($id)'); assert('is_string($data)'); assert('is_int($expires)'); assert('is_null($tag) || is_string($tag)'); $cacheFile = $this->cacheDirectory . '/' . $id; try { SimpleSAML_Utilities::writeFile($cacheFile, $data); } catch (Exception $e) { SimpleSAML_Logger::warning($this->logLoc . 'Unable to write to cache file ' . var_export($cacheFile, TRUE)); return; } $expireInfo = (string)$expires; if ($tag !== NULL) { $expireInfo .= ':' . $tag; } $expireFile = $cacheFile . '.expire'; try { SimpleSAML_Utilities::writeFile($expireFile, $expireInfo); } catch (Exception $e) { SimpleSAML_Logger::warning($this->logLoc . 'Unable to write expiration info to ' . var_export($expireFile, TRUE)); return $metadata; } } /** * Check validity of cached data. * * @param string $id The identifier of this data. * @param string $tag The tag that was passed to addCacheItem. * @return bool TRUE if the data is valid, FALSE if not. */ public function isCacheValid($id, $tag = NULL) { assert('is_string($id)'); assert('is_null($tag) || is_string($tag)'); $cacheFile = $this->cacheDirectory . '/' . $id; if (!file_exists($cacheFile)) { return FALSE; } $expireFile = $cacheFile . '.expire'; if (!file_exists($expireFile)) { return FALSE; } $expireData = @file_get_contents($expireFile); if ($expireData === FALSE) { return FALSE; } $expireData = explode(':', $expireData, 2); $expireTime = (int)$expireData[0]; if ($expireTime <= time()) { return FALSE; } if (count($expireData) === 1) { $expireTag = NULL; } else { $expireTag = $expireData[1]; } if ($expireTag !== $tag) { return FALSE; } return TRUE; } /** * Get the cache item. * * @param string $id The identifier of this data. * @param string $tag The tag that was passed to addCacheItem. * @return string|NULL The cache item, or NULL if it isn't cached or if it is expired. */ public function getCacheItem($id, $tag = NULL) { assert('is_string($id)'); assert('is_null($tag) || is_string($tag)'); if (!$this->isCacheValid($id, $tag)) { return NULL; } $cacheFile = $this->cacheDirectory . '/' . $id; return @file_get_contents($cacheFile); } /** * Get the cache filename for the specific id. * * @param string $id The identifier of the cached data. * @return string|NULL The filename, or NULL if the cache file doesn't exist. */ public function getCacheFile($id) { assert('is_string($id)'); $cacheFile = $this->cacheDirectory . '/' . $id; if (!file_exists($cacheFile)) { return NULL; } return $cacheFile; } /** * Retrieve the SSL CA file path, if it is set. * * @return string|NULL The SSL CA file path. */ public function getCAFile() { return $this->sslCAFile; } /** * Sign the generated EntitiesDescriptor. */ protected function addSignature(SAML2_SignedElement $element) { if ($this->signKey === NULL) { return; } $privateKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'private')); if ($this->signKeyPass !== NULL) { $privateKey->passphrase = $this->signKeyPass; } $privateKey->loadKey($this->signKey, FALSE); $element->setSignatureKey($privateKey); if ($this->signCert !== NULL) { $element->setCertificates(array($this->signCert)); } } /** * Retrieve all entities as an EntitiesDescriptor. * * @return SAML2_XML_md_EntitiesDescriptor The entities. */ protected function getEntitiesDescriptor() { $ret = new SAML2_XML_md_EntitiesDescriptor(); $now = time(); // add RegistrationInfo extension if enabled if ($this->regInfo !== NULL) { $ri = new SAML2_XML_mdrpi_RegistrationInfo(); $ri->registrationInstant = $now; foreach ($this->regInfo as $riName => $riValues) { switch ($riName) { case 'authority': $ri->registrationAuthority = $riValues; break; case 'instant': $ri->registrationInstant = SAML2_Utils::xsDateTimeToTimestamp($riValues); break; case 'policies': $ri->RegistrationPolicy = $riValues; break; } } $ret->Extensions[] = $ri; } foreach ($this->sources as $source) { $m = $source->getMetadata(); if ($m === NULL) { continue; } $ret->children[] = $m; } $ret->validUntil = $now + $this->validLength; return $ret; } /** * Retrieve the complete, signed metadata as text. * * This function will write the new metadata to the cache file, but will not return * the cached metadata. * * @return string The metadata, as text. */ public function updateCachedMetadata() { $ed = $this->getEntitiesDescriptor(); $this->addSignature($ed); $xml = $ed->toXML(); $xml = $xml->ownerDocument->saveXML($xml); if ($this->cacheGenerated !== NULL) { SimpleSAML_Logger::debug($this->logLoc . 'Saving generated metadata to cache.'); $this->addCacheItem($this->cacheId, $xml, time() + $this->cacheGenerated, $this->cacheTag); } return $xml; } /** * Retrieve the complete, signed metadata as text. * * @return string The metadata, as text. */ public function getMetadata() { if ($this->cacheGenerated !== NULL) { $xml = $this->getCacheItem($this->cacheId, $this->cacheTag); if ($xml !== NULL) { SimpleSAML_Logger::debug($this->logLoc . 'Loaded generated metadata from cache.'); return $xml; } } return $this->updateCachedMetadata(); } /** * Update the cached copy of our metadata. */ public function updateCache() { foreach ($this->sources as $source) { $source->updateCache(); } $this->updateCachedMetadata(); } }