ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
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 
267  public function getMetaData($index, $set)
268  {
269  assert('is_string($index)');
270  assert('is_string($set)');
271 
272  Logger::info(__CLASS__.': loading metadata entity ['.$index.'] from ['.$set.']');
273 
274  // read from cache if possible
275  $data = $this->getFromCache($set, $index);
276 
277  if ($data !== null && array_key_exists('expires', $data) && $data['expires'] < time()) {
278  // metadata has expired
279  $data = null;
280  }
281 
282  if (isset($data)) {
283  // metadata found in cache and not expired
284  Logger::debug(__CLASS__.': using cached metadata for: '.$index.'.');
285  return $data;
286  }
287 
288  // look at Metadata Query Protocol: https://github.com/iay/md-query/blob/master/draft-young-md-query.txt
289  $mdq_url = $this->server.'/entities/'.urlencode($index);
290 
291  Logger::debug(__CLASS__.': downloading metadata for "'.$index.'" from ['.$mdq_url.']');
292  try {
293  $xmldata = HTTP::fetch($mdq_url);
294  } catch (\Exception $e) {
295  Logger::warning('Fetching metadata for '.$index.': '.$e->getMessage());
296  }
297 
298  if (empty($xmldata)) {
299  $error = error_get_last();
300  throw new \Exception(
301  'Error downloading metadata for "'.$index.'" from "'.$mdq_url.'": '.$error['message']
302  );
303  }
304 
306  $entity = \SimpleSAML_Metadata_SAMLParser::parseString($xmldata);
307  Logger::debug(__CLASS__.': completed parsing of ['.$mdq_url.']');
308 
309  if ($this->validateFingerprint !== null) {
310  if (!$entity->validateFingerprint($this->validateFingerprint)) {
311  throw new \Exception(__CLASS__.': error, could not verify signature for entity: '.$index.'".');
312  }
313  }
314 
315  $data = self::getParsedSet($entity, $set);
316  if ($data === null) {
317  throw new \Exception(__CLASS__.': no metadata for set "'.$set.'" available from "'.$index.'".');
318  }
319 
320  $this->writeToCache($set, $index, $data);
321 
322  return $data;
323  }
324 }
writeToCache($set, $entityId, $data)
Save a entity to the cache.
Definition: MDQ.php:197
getMetadata1xSP()
This function returns the metadata for SAML 1.x SPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:538
static getParsedSet(\SimpleSAML_Metadata_SAMLParser $entity, $set)
Retrieve metadata for the correct set from a SAML2Parser.
Definition: MDQ.php:225
getFromCache($set, $entityId)
Load a entity from the cache.
Definition: MDQ.php:139
__construct($config)
This function initializes the dynamic XML metadata source.
Definition: MDQ.php:66
getMetadataSet($set)
This function is not implemented.
Definition: MDQ.php:104
static debug($string)
Definition: Logger.php:213
$index
Definition: metadata.php:60
getAttributeAuthorities()
Retrieve AttributeAuthorities from the metadata.
Definition: SAMLParser.php:823
$error
Definition: Error.php:17
getMetadata1xIdP()
This function returns the metadata for SAML 1.x IdPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:612
static warning($string)
Definition: Logger.php:179
getCacheFilename($set, $entityId)
Find the cache file name for an entity,.
Definition: MDQ.php:119
getMetadata20IdP()
This function returns the metadata for SAML 2.0 IdPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:765
Create styles array
The data for the language used.
if($source===null) if(!($source instanceof sspmod_saml_Auth_Source_SP)) $entityId
Definition: metadata.php:22
$globalConfig
This is class for parsing of SAML 1.x and SAML 2.0 metadata.
Definition: SAMLParser.php:15
$ret
Definition: parser.php:6
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
getMetadata20SP()
This function returns the metadata for SAML 2.0 SPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:668
static getInstance($instancename='simplesaml')
Get a configuration file by its instance name.