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}
An exception for terminatinating execution or to throw for unit testing.
static warning($string)
Definition: Logger.php:179
static debug($string)
Definition: Logger.php:213
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:538
getAttributeAuthorities()
Retrieve AttributeAuthorities from the metadata.
Definition: SAMLParser.php:823
getMetadata20SP()
This function returns the metadata for SAML 2.0 SPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:668
getMetadata20IdP()
This function returns the metadata for SAML 2.0 IdPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:765
getMetadata1xIdP()
This function returns the metadata for SAML 1.x IdPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:612
error($a_errmsg)
set error message @access public
$error
Definition: Error.php:17
$index
Definition: metadata.php:60
if( $source===null) if(!($source instanceof sspmod_saml_Auth_Source_SP)) $entityId
Definition: metadata.php:22
$ret
Definition: parser.php:6
$globalConfig