ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
MDQ.php
Go to the documentation of this file.
1<?php
2
4
7
17{
18
24 private $server;
25
33
39 private $cacheDir;
40
41
47 private $cacheLength;
48
49
66 protected function __construct($config)
67 {
68 assert(is_array($config));
69
70 if (!array_key_exists('server', $config)) {
71 throw new \Exception(__CLASS__.": the 'server' configuration option is not set.");
72 } else {
73 $this->server = $config['server'];
74 }
75
76 if (array_key_exists('validateFingerprint', $config)) {
77 $this->validateFingerprint = $config['validateFingerprint'];
78 } else {
79 $this->validateFingerprint = null;
80 }
81
82 if (array_key_exists('cachedir', $config)) {
84 $this->cacheDir = $globalConfig->resolvePath($config['cachedir']);
85 } else {
86 $this->cacheDir = null;
87 }
88
89 if (array_key_exists('cachelength', $config)) {
90 $this->cacheLength = $config['cachelength'];
91 } else {
92 $this->cacheLength = 86400;
93 }
94 }
95
96
104 public function getMetadataSet($set)
105 {
106 // we don't have this metadata set
107 return array();
108 }
109
110
119 private function getCacheFilename($set, $entityId)
120 {
121 assert(is_string($set));
122 assert(is_string($entityId));
123
124 $cachekey = sha1($entityId);
125 return $this->cacheDir.'/'.$set.'-'.$cachekey.'.cached.xml';
126 }
127
128
139 private function getFromCache($set, $entityId)
140 {
141 assert(is_string($set));
142 assert(is_string($entityId));
143
144 if (empty($this->cacheDir)) {
145 return null;
146 }
147
148 $cachefilename = $this->getCacheFilename($set, $entityId);
149 if (!file_exists($cachefilename)) {
150 return null;
151 }
152 if (!is_readable($cachefilename)) {
153 throw new \Exception(__CLASS__.': could not read cache file for entity ['.$cachefilename.']');
154 }
155 Logger::debug(__CLASS__.': reading cache ['.$entityId.'] => ['.$cachefilename.']');
156
157 /* Ensure that this metadata isn't older that the cachelength option allows. This
158 * must be verified based on the file, since this option may be changed after the
159 * file is written.
160 */
161 $stat = stat($cachefilename);
162 if ($stat['mtime'] + $this->cacheLength <= time()) {
163 Logger::debug(__CLASS__.': cache file older that the cachelength option allows.');
164 return null;
165 }
166
167 $rawData = file_get_contents($cachefilename);
168 if (empty($rawData)) {
169 $error = error_get_last();
170 throw new \Exception(
171 __CLASS__.': error reading metadata from cache file "'.$cachefilename.'": '.$error['message']
172 );
173 }
174
175 $data = unserialize($rawData);
176 if ($data === false) {
177 throw new \Exception(__CLASS__.': error unserializing cached data from file "'.$cachefilename.'".');
178 }
179
180 if (!is_array($data)) {
181 throw new \Exception(__CLASS__.': Cached metadata from "'.$cachefilename.'" wasn\'t an array.');
182 }
183
184 return $data;
185 }
186
187
197 private function writeToCache($set, $entityId, $data)
198 {
199 assert(is_string($set));
200 assert(is_string($entityId));
201 assert(is_array($data));
202
203 if (empty($this->cacheDir)) {
204 return;
205 }
206
207 $cachefilename = $this->getCacheFilename($set, $entityId);
208 if (!is_writable(dirname($cachefilename))) {
209 throw new \Exception(__CLASS__.': could not write cache file for entity ['.$cachefilename.']');
210 }
211 Logger::debug(__CLASS__.': Writing cache ['.$entityId.'] => ['.$cachefilename.']');
212 file_put_contents($cachefilename, serialize($data));
213 }
214
215
225 private static function getParsedSet(\SimpleSAML_Metadata_SAMLParser $entity, $set)
226 {
227 assert(is_string($set));
228
229 switch ($set) {
230 case 'saml20-idp-remote':
231 return $entity->getMetadata20IdP();
232 case 'saml20-sp-remote':
233 return $entity->getMetadata20SP();
234 case 'shib13-idp-remote':
235 return $entity->getMetadata1xIdP();
236 case 'shib13-sp-remote':
237 return $entity->getMetadata1xSP();
238 case 'attributeauthority-remote':
239 $ret = $entity->getAttributeAuthorities();
240 return $ret[0];
241
242 default:
243 Logger::warning(__CLASS__.': unknown metadata set: \''.$set.'\'.');
244 }
245
246 return null;
247 }
248
249
268 public function getMetaData($index, $set)
269 {
270 assert(is_string($index));
271 assert(is_string($set));
272
273 Logger::info(__CLASS__.': loading metadata entity ['.$index.'] from ['.$set.']');
274
275 // read from cache if possible
276 try {
277 $data = $this->getFromCache($set, $index);
278 } catch (\Exception $e) {
279 Logger::error($e->getMessage());
280 // proceed with fetching metadata even if the cache is broken
281 $data = null;
282 }
283
284 if ($data !== null && array_key_exists('expires', $data) && $data['expires'] < time()) {
285 // metadata has expired
286 $data = null;
287 }
288
289 if (isset($data)) {
290 // metadata found in cache and not expired
291 Logger::debug(__CLASS__.': using cached metadata for: '.$index.'.');
292 return $data;
293 }
294
295 // look at Metadata Query Protocol: https://github.com/iay/md-query/blob/master/draft-young-md-query.txt
296 $mdq_url = $this->server.'/entities/'.urlencode($index);
297
298 Logger::debug(__CLASS__.': downloading metadata for "'.$index.'" from ['.$mdq_url.']');
299 try {
300 $xmldata = HTTP::fetch($mdq_url);
301 } catch (\Exception $e) {
302 // Avoid propagating the exception, make sure we can handle the error later
303 $xmldata = false;
304 }
305
306 if (empty($xmldata)) {
307 $error = error_get_last();
308 Logger::info('Unable to fetch metadata for "'.$index.'" from '.$mdq_url.': '.
309 (is_array($error) ? $error['message'] : 'no error available'));
310 return null;
311 }
312
314 $entity = \SimpleSAML_Metadata_SAMLParser::parseString($xmldata);
315 Logger::debug(__CLASS__.': completed parsing of ['.$mdq_url.']');
316
317 if ($this->validateFingerprint !== null) {
318 if (!$entity->validateFingerprint($this->validateFingerprint)) {
319 throw new \Exception(__CLASS__.': error, could not verify signature for entity: '.$index.'".');
320 }
321 }
322
323 $data = self::getParsedSet($entity, $set);
324 if ($data === null) {
325 throw new \Exception(__CLASS__.': no metadata for set "'.$set.'" available from "'.$index.'".');
326 }
327
328 try {
329 $this->writeToCache($set, $index, $data);
330 } catch (\Exception $e) {
331 // Proceed without writing to cache
332 Logger::error('Error writing MDQ result to cache: '.$e->getMessage());
333 }
334
335 return $data;
336 }
337}
An exception for terminatinating execution or to throw for unit testing.
static warning($string)
Definition: Logger.php:177
static debug($string)
Definition: Logger.php:211
getFromCache($set, $entityId)
Load a entity from the cache.
Definition: MDQ.php:139
static getParsedSet(\SimpleSAML_Metadata_SAMLParser $entity, $set)
Retrieve metadata for the correct set from a SAML2Parser.
Definition: MDQ.php:225
getMetadataSet($set)
This function is not implemented.
Definition: MDQ.php:104
writeToCache($set, $entityId, $data)
Save a entity to the cache.
Definition: MDQ.php:197
getCacheFilename($set, $entityId)
Find the cache file name for an entity,.
Definition: MDQ.php:119
__construct($config)
This function initializes the dynamic XML metadata source.
Definition: MDQ.php:66
static getInstance($instancename='simplesaml')
Get a configuration file by its instance name.
This is class for parsing of SAML 1.x and SAML 2.0 metadata.
Definition: SAMLParser.php:16
getMetadata1xSP()
This function returns the metadata for SAML 1.x SPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:520
getAttributeAuthorities()
Retrieve AttributeAuthorities from the metadata.
Definition: SAMLParser.php:811
getMetadata20SP()
This function returns the metadata for SAML 2.0 SPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:650
getMetadata20IdP()
This function returns the metadata for SAML 2.0 IdPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:753
getMetadata1xIdP()
This function returns the metadata for SAML 1.x IdPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:594
error($a_errmsg)
set error message @access public
$config
Definition: bootstrap.php:15
if( $source===null) if(!($source instanceof sspmod_saml_Auth_Source_SP)) $entityId
Definition: metadata.php:22
$ret
Definition: parser.php:6
$data
Definition: bench.php:6
$globalConfig