Passed
Push — master ( 87f03c...4cefa8 )
by Tim
01:57
created
lib/Aggregator.php 2 patches
Spacing   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -190,7 +190,7 @@  discard block
 block discarded – undo
190 190
         assert('is_string($id)');
191 191
 
192 192
         $this->id = $id;
193
-        $this->logLoc = 'aggregator2:'.$this->id.': ';
193
+        $this->logLoc = 'aggregator2:' . $this->id . ': ';
194 194
 
195 195
         $this->cronTag = $config->getString('cron.tag', null);
196 196
 
@@ -221,7 +221,7 @@  discard block
 block discarded – undo
221 221
             $signKey = System::resolvePath($signKey, $certDir);
222 222
             $this->signKey = @file_get_contents($signKey);
223 223
             if ($this->signKey === null) {
224
-                throw new Exception('Unable to load private key from '.var_export($signKey, true));
224
+                throw new Exception('Unable to load private key from ' . var_export($signKey, true));
225 225
             }
226 226
         }
227 227
 
@@ -232,13 +232,13 @@  discard block
 block discarded – undo
232 232
             $signCert = System::resolvePath($signCert, $certDir);
233 233
             $this->signCert = @file_get_contents($signCert);
234 234
             if ($this->signCert === null) {
235
-                throw new Exception('Unable to load certificate file from '.var_export($signCert, true));
235
+                throw new Exception('Unable to load certificate file from ' . var_export($signCert, true));
236 236
             }
237 237
         }
238 238
 
239 239
         $this->signAlg = $config->getString('sign.algorithm', XMLSecurityKey::RSA_SHA1);
240 240
         if (!in_array($this->signAlg, self::$SUPPORTED_SIGNATURE_ALGORITHMS)) {
241
-            throw new Exception('Unsupported signature algorithm '.var_export($this->signAlg, true));
241
+            throw new Exception('Unsupported signature algorithm ' . var_export($this->signAlg, true));
242 242
         }
243 243
 
244 244
         $this->sslCAFile = $config->getString('ssl.cafile', null);
@@ -304,24 +304,24 @@  discard block
 block discarded – undo
304 304
         assert('is_int($expires)');
305 305
         assert('is_null($tag) || is_string($tag)');
306 306
 
307
-        $cacheFile = $this->cacheDirectory.'/'.$id;
307
+        $cacheFile = $this->cacheDirectory . '/' . $id;
308 308
         try {
309 309
             System::writeFile($cacheFile, $data);
310 310
         } catch (\Exception $e) {
311
-            Logger::warning($this->logLoc.'Unable to write to cache file '.var_export($cacheFile, true));
311
+            Logger::warning($this->logLoc . 'Unable to write to cache file ' . var_export($cacheFile, true));
312 312
             return;
313 313
         }
314 314
 
315
-        $expireInfo = (string)$expires;
315
+        $expireInfo = (string) $expires;
316 316
         if ($tag !== null) {
317
-            $expireInfo .= ':'.$tag;
317
+            $expireInfo .= ':' . $tag;
318 318
         }
319 319
 
320
-        $expireFile = $cacheFile.'.expire';
320
+        $expireFile = $cacheFile . '.expire';
321 321
         try {
322 322
             System::writeFile($expireFile, $expireInfo);
323 323
         } catch (\Exception $e) {
324
-            Logger::warning($this->logLoc.'Unable to write expiration info to '.var_export($expireFile, true));
324
+            Logger::warning($this->logLoc . 'Unable to write expiration info to ' . var_export($expireFile, true));
325 325
         }
326 326
     }
327 327
 
@@ -338,12 +338,12 @@  discard block
 block discarded – undo
338 338
         assert('is_string($id)');
339 339
         assert('is_null($tag) || is_string($tag)');
340 340
 
341
-        $cacheFile = $this->cacheDirectory.'/'.$id;
341
+        $cacheFile = $this->cacheDirectory . '/' . $id;
342 342
         if (!file_exists($cacheFile)) {
343 343
             return false;
344 344
         }
345 345
 
346
-        $expireFile = $cacheFile.'.expire';
346
+        $expireFile = $cacheFile . '.expire';
347 347
         if (!file_exists($expireFile)) {
348 348
             return false;
349 349
         }
@@ -389,7 +389,7 @@  discard block
 block discarded – undo
389 389
             return null;
390 390
         }
391 391
 
392
-        $cacheFile = $this->cacheDirectory.'/'.$id;
392
+        $cacheFile = $this->cacheDirectory . '/' . $id;
393 393
         return @file_get_contents($cacheFile);
394 394
     }
395 395
 
@@ -404,7 +404,7 @@  discard block
 block discarded – undo
404 404
     {
405 405
         assert('is_string($id)');
406 406
 
407
-        $cacheFile = $this->cacheDirectory.'/'.$id;
407
+        $cacheFile = $this->cacheDirectory . '/' . $id;
408 408
         if (!file_exists($cacheFile)) {
409 409
             return null;
410 410
         }
@@ -616,7 +616,7 @@  discard block
 block discarded – undo
616 616
         }
617 617
         $this->excluded = $entities;
618 618
         sort($this->excluded);
619
-        $this->cacheId = sha1($this->cacheId.serialize($this->excluded));
619
+        $this->cacheId = sha1($this->cacheId . serialize($this->excluded));
620 620
     }
621 621
 
622 622
 
@@ -675,7 +675,7 @@  discard block
 block discarded – undo
675 675
         $options = ['saml2', 'shib13', 'saml20-aa', 'shib13-aa'];
676 676
         $this->roles['SAML2_XML_md_AttributeAuthorityDescriptor'] = (array_intersect($set, $options) !== []);
677 677
 
678
-        $this->cacheId = sha1($this->cacheId.serialize($this->protocols).serialize($this->roles));
678
+        $this->cacheId = sha1($this->cacheId . serialize($this->protocols) . serialize($this->roles));
679 679
     }
680 680
 
681 681
 
@@ -698,7 +698,7 @@  discard block
 block discarded – undo
698 698
         $xml = $xml->ownerDocument->saveXML($xml);
699 699
 
700 700
         if ($this->cacheGenerated !== null) {
701
-            Logger::debug($this->logLoc.'Saving generated metadata to cache.');
701
+            Logger::debug($this->logLoc . 'Saving generated metadata to cache.');
702 702
             $this->addCacheItem($this->cacheId, $xml, time() + $this->cacheGenerated, $this->cacheTag);
703 703
         }
704 704
 
@@ -716,7 +716,7 @@  discard block
 block discarded – undo
716 716
         if ($this->cacheGenerated !== null) {
717 717
             $xml = $this->getCacheItem($this->cacheId, $this->cacheTag);
718 718
             if ($xml !== null) {
719
-                Logger::debug($this->logLoc.'Loaded generated metadata from cache.');
719
+                Logger::debug($this->logLoc . 'Loaded generated metadata from cache.');
720 720
                 return $xml;
721 721
             }
722 722
         }
Please login to merge, or discard this patch.
Indentation   +714 added lines, -714 removed lines patch added patch discarded remove patch
@@ -23,718 +23,718 @@
 block discarded – undo
23 23
  */
24 24
 class Aggregator
25 25
 {
26
-    /**
27
-     * The list of signature algorithms supported by the aggregator.
28
-     *
29
-     * @var array
30
-     */
31
-    public static $SUPPORTED_SIGNATURE_ALGORITHMS = [
32
-        XMLSecurityKey::RSA_SHA1,
33
-        XMLSecurityKey::RSA_SHA256,
34
-        XMLSecurityKey::RSA_SHA384,
35
-        XMLSecurityKey::RSA_SHA512,
36
-    ];
37
-
38
-    /**
39
-     * The ID of this aggregator.
40
-     *
41
-     * @var string
42
-     */
43
-    protected $id;
44
-
45
-    /**
46
-     * Our log "location".
47
-     *
48
-     * @var string
49
-     */
50
-    protected $logLoc;
51
-
52
-    /**
53
-     * Which cron-tag this should be updated in.
54
-     *
55
-     * @var string|null
56
-     */
57
-    protected $cronTag;
58
-
59
-    /**
60
-     * Absolute path to a cache directory.
61
-     *
62
-     * @var string|null
63
-     */
64
-    protected $cacheDirectory;
65
-
66
-    /**
67
-     * The entity sources.
68
-     *
69
-     * Array of sspmod_aggregator2_EntitySource objects.
70
-     *
71
-     * @var array
72
-     */
73
-    protected $sources = [];
74
-
75
-    /**
76
-     * How long the generated metadata should be valid, as a number of seconds.
77
-     *
78
-     * This is used to set the validUntil attribute on the generated EntityDescriptor.
79
-     *
80
-     * @var int
81
-     */
82
-    protected $validLength;
83
-
84
-    /**
85
-     * Duration we should cache generated metadata.
86
-     *
87
-     * @var int
88
-     */
89
-    protected $cacheGenerated;
90
-
91
-    /**
92
-     * An array of entity IDs to exclude from the aggregate.
93
-     *
94
-     * @var string[]|null
95
-     */
96
-    protected $excluded;
97
-
98
-    /**
99
-     * An indexed array of protocols to filter the aggregate by. keys can be any of:
100
-     *
101
-     * - urn:oasis:names:tc:SAML:1.1:protocol
102
-     * - urn:oasis:names:tc:SAML:2.0:protocol
103
-     *
104
-     * Values will be true if enabled, false otherwise.
105
-     *
106
-     * @var array|null
107
-     */
108
-    protected $protocols;
109
-
110
-    /**
111
-     * An array of roles to filter the aggregate by. Keys can be any of:
112
-     *
113
-     * - SAML2_XML_md_IDPSSODescriptor
114
-     * - SAML2_XML_md_SPSSODescriptor
115
-     * - SAML2_XML_md_AttributeAuthorityDescriptor
116
-     *
117
-     * Values will be true if enabled, false otherwise.
118
-     *
119
-     * @var array|null
120
-     */
121
-    protected $roles;
122
-
123
-    /**
124
-     * The key we should use to sign the metadata.
125
-     *
126
-     * @var string|null
127
-     */
128
-    protected $signKey;
129
-
130
-    /**
131
-     * The password for the private key.
132
-     *
133
-     * @var string|null
134
-     */
135
-    protected $signKeyPass;
136
-
137
-    /**
138
-     * The certificate of the key we sign the metadata with.
139
-     *
140
-     * @var string|null
141
-     */
142
-    protected $signCert;
143
-
144
-    /**
145
-     * The algorithm to use for metadata signing.
146
-     *
147
-     * @var string|null
148
-     */
149
-    protected $signAlg;
150
-
151
-    /**
152
-     * The CA certificate file that should be used to validate https-connections.
153
-     *
154
-     * @var string|null
155
-     */
156
-    protected $sslCAFile;
157
-
158
-    /**
159
-     * The cache ID for our generated metadata.
160
-     *
161
-     * @var string
162
-     */
163
-    protected $cacheId;
164
-
165
-    /**
166
-     * The cache tag for our generated metadata.
167
-     *
168
-     * This tag is used to make sure that a config change
169
-     * invalidates our cached metadata.
170
-     *
171
-     * @var string
172
-     */
173
-    protected $cacheTag;
174
-
175
-    /**
176
-     * The registration information for our generated metadata.
177
-     *
178
-     * @var array
179
-     */
180
-    protected $regInfo;
181
-
182
-
183
-    /**
184
-     * Initialize this aggregator.
185
-     *
186
-     * @param string $id  The id of this aggregator.
187
-     * @param \SimpleSAML\Configuration $config  The configuration for this aggregator.
188
-     */
189
-    protected function __construct($id, Configuration $config)
190
-    {
191
-        assert('is_string($id)');
192
-
193
-        $this->id = $id;
194
-        $this->logLoc = 'aggregator2:'.$this->id.': ';
195
-
196
-        $this->cronTag = $config->getString('cron.tag', null);
197
-
198
-        $this->cacheDirectory = $config->getString('cache.directory', null);
199
-        if ($this->cacheDirectory !== null) {
200
-            $this->cacheDirectory = System::resolvePath($this->cacheDirectory);
201
-        }
202
-
203
-        $this->cacheGenerated = $config->getInteger('cache.generated', null);
204
-        if ($this->cacheGenerated !== null) {
205
-            $this->cacheId = sha1($this->id);
206
-            $this->cacheTag = sha1(serialize($config));
207
-        }
208
-
209
-        // configure entity IDs excluded by default
210
-        $this->excludeEntities($config->getArrayize('exclude', null));
211
-
212
-        // configure filters
213
-        $this->setFilters($config->getArrayize('filter', null));
214
-
215
-        $this->validLength = $config->getInteger('valid.length', 7*24*60*60);
216
-
217
-        $globalConfig = Configuration::getInstance();
218
-        $certDir = $globalConfig->getPathValue('certdir', 'cert/');
219
-
220
-        $signKey = $config->getString('sign.privatekey', null);
221
-        if ($signKey !== null) {
222
-            $signKey = System::resolvePath($signKey, $certDir);
223
-            $this->signKey = @file_get_contents($signKey);
224
-            if ($this->signKey === null) {
225
-                throw new Exception('Unable to load private key from '.var_export($signKey, true));
226
-            }
227
-        }
228
-
229
-        $this->signKeyPass = $config->getString('sign.privatekey_pass', null);
230
-
231
-        $signCert = $config->getString('sign.certificate', null);
232
-        if ($signCert !== null) {
233
-            $signCert = System::resolvePath($signCert, $certDir);
234
-            $this->signCert = @file_get_contents($signCert);
235
-            if ($this->signCert === null) {
236
-                throw new Exception('Unable to load certificate file from '.var_export($signCert, true));
237
-            }
238
-        }
239
-
240
-        $this->signAlg = $config->getString('sign.algorithm', XMLSecurityKey::RSA_SHA1);
241
-        if (!in_array($this->signAlg, self::$SUPPORTED_SIGNATURE_ALGORITHMS)) {
242
-            throw new Exception('Unsupported signature algorithm '.var_export($this->signAlg, true));
243
-        }
244
-
245
-        $this->sslCAFile = $config->getString('ssl.cafile', null);
246
-
247
-        $this->regInfo = $config->getArray('RegistrationInfo', null);
248
-
249
-        $this->initSources($config->getConfigList('sources'));
250
-    }
251
-
252
-
253
-    /**
254
-     * Populate the sources array.
255
-     *
256
-     * This is called from the constructor, and can be overridden in subclasses.
257
-     *
258
-     * @param array $sources  The sources as an array of SimpleSAML_Configuration objects.
259
-     */
260
-    protected function initSources(array $sources)
261
-    {
262
-        foreach ($sources as $source) {
263
-            $this->sources[] = new EntitySource($this, $source);
264
-        }
265
-    }
266
-
267
-
268
-    /**
269
-     * Return an instance of the aggregator with the given id.
270
-     *
271
-     * @param string $id  The id of the aggregator.
272
-     */
273
-    public static function getAggregator($id)
274
-    {
275
-        assert('is_string($id)');
276
-
277
-        $config = Configuration::getConfig('module_aggregator2.php');
278
-        return new Aggregator($id, $config->getConfigItem($id));
279
-    }
280
-
281
-
282
-    /**
283
-     * Retrieve the ID of the aggregator.
284
-     *
285
-     * @return string  The ID of this aggregator.
286
-     */
287
-    public function getId()
288
-    {
289
-        return $this->id;
290
-    }
291
-
292
-
293
-    /**
294
-     * Add an item to the cache.
295
-     *
296
-     * @param string $id  The identifier of this data.
297
-     * @param string $data  The data.
298
-     * @param int $expires  The timestamp the data expires.
299
-     * @param string|null $tag  An extra tag that can be used to verify the validity of the cached data.
300
-     */
301
-    public function addCacheItem($id, $data, $expires, $tag = null)
302
-    {
303
-        assert('is_string($id)');
304
-        assert('is_string($data)');
305
-        assert('is_int($expires)');
306
-        assert('is_null($tag) || is_string($tag)');
307
-
308
-        $cacheFile = $this->cacheDirectory.'/'.$id;
309
-        try {
310
-            System::writeFile($cacheFile, $data);
311
-        } catch (\Exception $e) {
312
-            Logger::warning($this->logLoc.'Unable to write to cache file '.var_export($cacheFile, true));
313
-            return;
314
-        }
315
-
316
-        $expireInfo = (string)$expires;
317
-        if ($tag !== null) {
318
-            $expireInfo .= ':'.$tag;
319
-        }
320
-
321
-        $expireFile = $cacheFile.'.expire';
322
-        try {
323
-            System::writeFile($expireFile, $expireInfo);
324
-        } catch (\Exception $e) {
325
-            Logger::warning($this->logLoc.'Unable to write expiration info to '.var_export($expireFile, true));
326
-        }
327
-    }
328
-
329
-
330
-    /**
331
-     * Check validity of cached data.
332
-     *
333
-     * @param string $id  The identifier of this data.
334
-     * @param string $tag  The tag that was passed to addCacheItem.
335
-     * @return bool  TRUE if the data is valid, FALSE if not.
336
-     */
337
-    public function isCacheValid($id, $tag = null)
338
-    {
339
-        assert('is_string($id)');
340
-        assert('is_null($tag) || is_string($tag)');
341
-
342
-        $cacheFile = $this->cacheDirectory.'/'.$id;
343
-        if (!file_exists($cacheFile)) {
344
-            return false;
345
-        }
346
-
347
-        $expireFile = $cacheFile.'.expire';
348
-        if (!file_exists($expireFile)) {
349
-            return false;
350
-        }
351
-
352
-        $expireData = @file_get_contents($expireFile);
353
-        if ($expireData === false) {
354
-            return false;
355
-        }
356
-
357
-        $expireData = explode(':', $expireData, 2);
358
-
359
-        $expireTime = intval($expireData[0]);
360
-        if ($expireTime <= time()) {
361
-            return false;
362
-        }
363
-
364
-        if (count($expireData) === 1) {
365
-            $expireTag = null;
366
-        } else {
367
-            $expireTag = $expireData[1];
368
-        }
369
-        if ($expireTag !== $tag) {
370
-            return false;
371
-        }
372
-
373
-        return true;
374
-    }
375
-
376
-
377
-    /**
378
-     * Get the cache item.
379
-     *
380
-     * @param string $id  The identifier of this data.
381
-     * @param string $tag  The tag that was passed to addCacheItem.
382
-     * @return string|null  The cache item, or NULL if it isn't cached or if it is expired.
383
-     */
384
-    public function getCacheItem($id, $tag = null)
385
-    {
386
-        assert('is_string($id)');
387
-        assert('is_null($tag) || is_string($tag)');
388
-
389
-        if (!$this->isCacheValid($id, $tag)) {
390
-            return null;
391
-        }
392
-
393
-        $cacheFile = $this->cacheDirectory.'/'.$id;
394
-        return @file_get_contents($cacheFile);
395
-    }
396
-
397
-
398
-    /**
399
-     * Get the cache filename for the specific id.
400
-     *
401
-     * @param string $id  The identifier of the cached data.
402
-     * @return string|null  The filename, or NULL if the cache file doesn't exist.
403
-     */
404
-    public function getCacheFile($id)
405
-    {
406
-        assert('is_string($id)');
407
-
408
-        $cacheFile = $this->cacheDirectory.'/'.$id;
409
-        if (!file_exists($cacheFile)) {
410
-            return null;
411
-        }
412
-
413
-        return $cacheFile;
414
-    }
415
-
416
-
417
-    /**
418
-     * Retrieve the SSL CA file path, if it is set.
419
-     *
420
-     * @return string|null  The SSL CA file path.
421
-     */
422
-    public function getCAFile()
423
-    {
424
-        return $this->sslCAFile;
425
-    }
426
-
427
-
428
-    /**
429
-     * Sign the generated EntitiesDescriptor.
430
-     */
431
-    protected function addSignature(SignedElement $element)
432
-    {
433
-        if ($this->signKey === null) {
434
-            return;
435
-        }
436
-
437
-        $privateKey = new XMLSecurityKey($this->signAlg, ['type' => 'private']);
438
-        if ($this->signKeyPass !== null) {
439
-            $privateKey->passphrase = $this->signKeyPass;
440
-        }
441
-        $privateKey->loadKey($this->signKey, false);
442
-
443
-        $element->setSignatureKey($privateKey);
444
-
445
-        if ($this->signCert !== null) {
446
-            $element->setCertificates([$this->signCert]);
447
-        }
448
-    }
449
-
450
-
451
-    /**
452
-     * Recursively browse the children of an EntitiesDescriptor element looking for EntityDescriptor elements, and
453
-     * return an array containing all of them.
454
-     *
455
-     * @param \SAML2\XML\md\EntitiesDescriptor $entity The source EntitiesDescriptor that holds the entities to extract.
456
-     *
457
-     * @return array An array containing all the EntityDescriptors found.
458
-     */
459
-    private static function extractEntityDescriptors($entity)
460
-    {
461
-        assert('$entity instanceof EntitiesDescriptor');
462
-
463
-        if (!($entity instanceof EntitiesDescriptor)) {
464
-            return [];
465
-        }
466
-
467
-        $results = [];
468
-        foreach ($entity->children as $child) {
469
-            if ($child instanceof EntityDescriptor) {
470
-                $results[] = $child;
471
-                continue;
472
-            }
473
-
474
-            $results = array_merge($results, self::extractEntityDescriptors($child));
475
-        }
476
-        return $results;
477
-    }
478
-
479
-
480
-    /**
481
-     * Retrieve all entities as an EntitiesDescriptor.
482
-     *
483
-     * @return \SAML2\XML\md\EntitiesDescriptor  The entities.
484
-     */
485
-    protected function getEntitiesDescriptor()
486
-    {
487
-        $ret = new EntitiesDescriptor();
488
-        $now = time();
489
-
490
-        // add RegistrationInfo extension if enabled
491
-        if ($this->regInfo !== null) {
492
-            $ri = new RegistrationInfo();
493
-            $ri->registrationInstant = $now;
494
-            foreach ($this->regInfo as $riName => $riValues) {
495
-                switch ($riName) {
496
-                    case 'authority':
497
-                        $ri->registrationAuthority = $riValues;
498
-                        break;
499
-                    case 'instant':
500
-                        $ri->registrationInstant = Utils::xsDateTimeToTimestamp($riValues);
501
-                        break;
502
-                    case 'policies':
503
-                        $ri->RegistrationPolicy = $riValues;
504
-                        break;
505
-                }
506
-            }
507
-            $ret->Extensions[] = $ri;
508
-        }
509
-
510
-        foreach ($this->sources as $source) {
511
-            $m = $source->getMetadata();
512
-            if ($m === NULL) {
513
-                continue;
514
-            }
515
-            if ($m instanceof EntityDescriptor) {
516
-                $ret->children[] = $m;
517
-            } elseif ($m instanceof EntitiesDescriptor) {
518
-                $ret->children = array_merge($ret->children, self::extractEntityDescriptors($m));
519
-            }
520
-        }
521
-
522
-        $ret->children = array_unique($ret->children, SORT_REGULAR);
523
-        $ret->validUntil = $now + $this->validLength;
524
-
525
-        return $ret;
526
-    }
527
-
528
-
529
-    /**
530
-     * Recursively traverse the children of an EntitiesDescriptor, removing those entities listed in the $entities
531
-     * property. Returns the EntitiesDescriptor with the entities filtered out.
532
-     *
533
-     * @param \SAML2\XML\md\EntitiesDescriptor $descriptor The EntitiesDescriptor from where to exclude entities.
534
-     *
535
-     * @return \SAML2\XML\md\EntitiesDescriptor The EntitiesDescriptor with excluded entities filtered out.
536
-     */
537
-    protected function exclude(EntitiesDescriptor $descriptor)
538
-    {
539
-        if (empty($this->excluded)) {
540
-            return $descriptor;
541
-        }
542
-
543
-        $filtered = [];
544
-        foreach ($descriptor->children as $child) {
545
-            if ($child instanceof EntityDescriptor) {
546
-                if (in_array($child->entityID, $this->excluded)) {
547
-                    continue;
548
-                }
549
-                $filtered[] = $child;
550
-            }
551
-
552
-            if ($child instanceof EntitiesDescriptor) {
553
-                $filtered[] = $this->exclude($child);
554
-            }
555
-        }
556
-
557
-        $descriptor->children = $filtered;
558
-        return $descriptor;
559
-    }
560
-
561
-
562
-    /**
563
-     * Recursively traverse the children of an EntitiesDescriptor, keeping only those entities with the roles listed in
564
-     * the $roles property, and support for the protocols listed in the $protocols property. Returns the
565
-     * EntitiesDescriptor containing only those entities.
566
-     *
567
-     * @param \SAML2\XML\md\EntitiesDescriptor $descriptor The EntitiesDescriptor to filter.
568
-     *
569
-     * @return SAML2_XML_md_EntitiesDescriptor The EntitiesDescriptor with only the entities filtered.
570
-     */
571
-    protected function filter(EntitiesDescriptor $descriptor)
572
-    {
573
-        if ($this->roles === null || $this->protocols === null) {
574
-            return $descriptor;
575
-        }
576
-
577
-        $enabled_roles = array_keys($this->roles, true);
578
-        $enabled_protos = array_keys($this->protocols, true);
579
-
580
-        $filtered = [];
581
-        foreach ($descriptor->children as $child) {
582
-            if ($child instanceof EntityDescriptor) {
583
-                foreach ($child->RoleDescriptor as $role) {
584
-                    if (in_array(get_class($role), $enabled_roles)) {
585
-                        // we found a role descriptor that is enabled by our filters, check protocols
586
-                        if (array_intersect($enabled_protos, $role->protocolSupportEnumeration) !== []) {
587
-                            // it supports some protocol we have enabled, add it
588
-                            $filtered[] = $child;
589
-                            break;
590
-                        }
591
-                    }
592
-                }
593
-
594
-            }
595
-
596
-            if ($child instanceof EntitiesDescriptor) {
597
-                $filtered[] = $this->filter($child);
598
-            }
599
-        }
600
-
601
-        $descriptor->children = $filtered;
602
-        return $descriptor;
603
-    }
604
-
605
-
606
-    /**
607
-     * Set this aggregator to exclude a set of entities from the resulting aggregate.
608
-     *
609
-     * @param array|null $entities The entity IDs of the entities to exclude.
610
-     */
611
-    public function excludeEntities($entities)
612
-    {
613
-        assert('is_array($entities) || is_null($entities)');
614
-
615
-        if ($entities === null) {
616
-            return;
617
-        }
618
-        $this->excluded = $entities;
619
-        sort($this->excluded);
620
-        $this->cacheId = sha1($this->cacheId.serialize($this->excluded));
621
-    }
622
-
623
-
624
-    /**
625
-     * Set the internal filters according to one or more options:
626
-     *
627
-     * - 'saml2': all SAML2.0-capable entities.
628
-     * - 'shib13': all SHIB1.3-capable entities.
629
-     * - 'saml20-idp': all SAML2.0-capable identity providers.
630
-     * - 'saml20-sp': all SAML2.0-capable service providers.
631
-     * - 'saml20-aa': all SAML2.0-capable attribute authorities.
632
-     * - 'shib13-idp': all SHIB1.3-capable identity providers.
633
-     * - 'shib13-sp': all SHIB1.3-capable service providers.
634
-     * - 'shib13-aa': all SHIB1.3-capable attribute authorities.
635
-     *
636
-     * @param array|null $set An array of the different roles and protocols to filter by.
637
-     */
638
-    public function setFilters($set)
639
-    {
640
-        assert('is_array($set) || is_null($set)');
641
-
642
-        if ($set === null) {
643
-            return;
644
-        }
645
-
646
-        // configure filters
647
-        $this->protocols = [
648
-            Constants::NS_SAMLP                    => true,
649
-            'urn:oasis:names:tc:SAML:1.1:protocol' => true,
650
-        ];
651
-        $this->roles = [
652
-            'SAML2_XML_md_IDPSSODescriptor'             => true,
653
-            'SAML2_XML_md_SPSSODescriptor'              => true,
654
-            'SAML2_XML_md_AttributeAuthorityDescriptor' => true,
655
-        ];
656
-
657
-        // now translate from the options we have, to specific protocols and roles
658
-
659
-        // check SAML 2.0 protocol
660
-        $options = ['saml2', 'saml20-idp', 'saml20-sp', 'saml20-aa'];
661
-        $this->protocols[Constants::NS_SAMLP] = (array_intersect($set, $options) !== []);
662
-
663
-        // check SHIB 1.3 protocol
664
-        $options = ['shib13', 'shib13-idp', 'shib13-sp', 'shib13-aa'];
665
-        $this->protocols['urn:oasis:names:tc:SAML:1.1:protocol'] = (array_intersect($set, $options) !== []);
666
-
667
-        // check IdP
668
-        $options = ['saml2', 'shib13', 'saml20-idp', 'shib13-idp'];
669
-        $this->roles['SAML2_XML_md_IDPSSODescriptor'] = (array_intersect($set, $options) !== []);
670
-
671
-        // check SP
672
-        $options = ['saml2', 'shib13', 'saml20-sp', 'shib13-sp'];
673
-        $this->roles['SAML2_XML_md_SPSSODescriptor'] = (array_intersect($set, $options) !== []);
674
-
675
-        // check AA
676
-        $options = ['saml2', 'shib13', 'saml20-aa', 'shib13-aa'];
677
-        $this->roles['SAML2_XML_md_AttributeAuthorityDescriptor'] = (array_intersect($set, $options) !== []);
678
-
679
-        $this->cacheId = sha1($this->cacheId.serialize($this->protocols).serialize($this->roles));
680
-    }
681
-
682
-
683
-    /**
684
-     * Retrieve the complete, signed metadata as text.
685
-     *
686
-     * This function will write the new metadata to the cache file, but will not return
687
-     * the cached metadata.
688
-     *
689
-     * @return string  The metadata, as text.
690
-     */
691
-    public function updateCachedMetadata()
692
-    {
693
-        $ed = $this->getEntitiesDescriptor();
694
-        $ed = $this->exclude($ed);
695
-        $ed = $this->filter($ed);
696
-        $this->addSignature($ed);
697
-
698
-        $xml = $ed->toXML();
699
-        $xml = $xml->ownerDocument->saveXML($xml);
700
-
701
-        if ($this->cacheGenerated !== null) {
702
-            Logger::debug($this->logLoc.'Saving generated metadata to cache.');
703
-            $this->addCacheItem($this->cacheId, $xml, time() + $this->cacheGenerated, $this->cacheTag);
704
-        }
705
-
706
-        return $xml;
707
-    }
708
-
709
-
710
-    /**
711
-     * Retrieve the complete, signed metadata as text.
712
-     *
713
-     * @return string  The metadata, as text.
714
-     */
715
-    public function getMetadata()
716
-    {
717
-        if ($this->cacheGenerated !== null) {
718
-            $xml = $this->getCacheItem($this->cacheId, $this->cacheTag);
719
-            if ($xml !== null) {
720
-                Logger::debug($this->logLoc.'Loaded generated metadata from cache.');
721
-                return $xml;
722
-            }
723
-        }
724
-
725
-        return $this->updateCachedMetadata();
726
-    }
727
-
728
-
729
-    /**
730
-     * Update the cached copy of our metadata.
731
-     */
732
-    public function updateCache()
733
-    {
734
-        foreach ($this->sources as $source) {
735
-            $source->updateCache();
736
-        }
737
-
738
-        $this->updateCachedMetadata();
739
-    }
26
+	/**
27
+	 * The list of signature algorithms supported by the aggregator.
28
+	 *
29
+	 * @var array
30
+	 */
31
+	public static $SUPPORTED_SIGNATURE_ALGORITHMS = [
32
+		XMLSecurityKey::RSA_SHA1,
33
+		XMLSecurityKey::RSA_SHA256,
34
+		XMLSecurityKey::RSA_SHA384,
35
+		XMLSecurityKey::RSA_SHA512,
36
+	];
37
+
38
+	/**
39
+	 * The ID of this aggregator.
40
+	 *
41
+	 * @var string
42
+	 */
43
+	protected $id;
44
+
45
+	/**
46
+	 * Our log "location".
47
+	 *
48
+	 * @var string
49
+	 */
50
+	protected $logLoc;
51
+
52
+	/**
53
+	 * Which cron-tag this should be updated in.
54
+	 *
55
+	 * @var string|null
56
+	 */
57
+	protected $cronTag;
58
+
59
+	/**
60
+	 * Absolute path to a cache directory.
61
+	 *
62
+	 * @var string|null
63
+	 */
64
+	protected $cacheDirectory;
65
+
66
+	/**
67
+	 * The entity sources.
68
+	 *
69
+	 * Array of sspmod_aggregator2_EntitySource objects.
70
+	 *
71
+	 * @var array
72
+	 */
73
+	protected $sources = [];
74
+
75
+	/**
76
+	 * How long the generated metadata should be valid, as a number of seconds.
77
+	 *
78
+	 * This is used to set the validUntil attribute on the generated EntityDescriptor.
79
+	 *
80
+	 * @var int
81
+	 */
82
+	protected $validLength;
83
+
84
+	/**
85
+	 * Duration we should cache generated metadata.
86
+	 *
87
+	 * @var int
88
+	 */
89
+	protected $cacheGenerated;
90
+
91
+	/**
92
+	 * An array of entity IDs to exclude from the aggregate.
93
+	 *
94
+	 * @var string[]|null
95
+	 */
96
+	protected $excluded;
97
+
98
+	/**
99
+	 * An indexed array of protocols to filter the aggregate by. keys can be any of:
100
+	 *
101
+	 * - urn:oasis:names:tc:SAML:1.1:protocol
102
+	 * - urn:oasis:names:tc:SAML:2.0:protocol
103
+	 *
104
+	 * Values will be true if enabled, false otherwise.
105
+	 *
106
+	 * @var array|null
107
+	 */
108
+	protected $protocols;
109
+
110
+	/**
111
+	 * An array of roles to filter the aggregate by. Keys can be any of:
112
+	 *
113
+	 * - SAML2_XML_md_IDPSSODescriptor
114
+	 * - SAML2_XML_md_SPSSODescriptor
115
+	 * - SAML2_XML_md_AttributeAuthorityDescriptor
116
+	 *
117
+	 * Values will be true if enabled, false otherwise.
118
+	 *
119
+	 * @var array|null
120
+	 */
121
+	protected $roles;
122
+
123
+	/**
124
+	 * The key we should use to sign the metadata.
125
+	 *
126
+	 * @var string|null
127
+	 */
128
+	protected $signKey;
129
+
130
+	/**
131
+	 * The password for the private key.
132
+	 *
133
+	 * @var string|null
134
+	 */
135
+	protected $signKeyPass;
136
+
137
+	/**
138
+	 * The certificate of the key we sign the metadata with.
139
+	 *
140
+	 * @var string|null
141
+	 */
142
+	protected $signCert;
143
+
144
+	/**
145
+	 * The algorithm to use for metadata signing.
146
+	 *
147
+	 * @var string|null
148
+	 */
149
+	protected $signAlg;
150
+
151
+	/**
152
+	 * The CA certificate file that should be used to validate https-connections.
153
+	 *
154
+	 * @var string|null
155
+	 */
156
+	protected $sslCAFile;
157
+
158
+	/**
159
+	 * The cache ID for our generated metadata.
160
+	 *
161
+	 * @var string
162
+	 */
163
+	protected $cacheId;
164
+
165
+	/**
166
+	 * The cache tag for our generated metadata.
167
+	 *
168
+	 * This tag is used to make sure that a config change
169
+	 * invalidates our cached metadata.
170
+	 *
171
+	 * @var string
172
+	 */
173
+	protected $cacheTag;
174
+
175
+	/**
176
+	 * The registration information for our generated metadata.
177
+	 *
178
+	 * @var array
179
+	 */
180
+	protected $regInfo;
181
+
182
+
183
+	/**
184
+	 * Initialize this aggregator.
185
+	 *
186
+	 * @param string $id  The id of this aggregator.
187
+	 * @param \SimpleSAML\Configuration $config  The configuration for this aggregator.
188
+	 */
189
+	protected function __construct($id, Configuration $config)
190
+	{
191
+		assert('is_string($id)');
192
+
193
+		$this->id = $id;
194
+		$this->logLoc = 'aggregator2:'.$this->id.': ';
195
+
196
+		$this->cronTag = $config->getString('cron.tag', null);
197
+
198
+		$this->cacheDirectory = $config->getString('cache.directory', null);
199
+		if ($this->cacheDirectory !== null) {
200
+			$this->cacheDirectory = System::resolvePath($this->cacheDirectory);
201
+		}
202
+
203
+		$this->cacheGenerated = $config->getInteger('cache.generated', null);
204
+		if ($this->cacheGenerated !== null) {
205
+			$this->cacheId = sha1($this->id);
206
+			$this->cacheTag = sha1(serialize($config));
207
+		}
208
+
209
+		// configure entity IDs excluded by default
210
+		$this->excludeEntities($config->getArrayize('exclude', null));
211
+
212
+		// configure filters
213
+		$this->setFilters($config->getArrayize('filter', null));
214
+
215
+		$this->validLength = $config->getInteger('valid.length', 7*24*60*60);
216
+
217
+		$globalConfig = Configuration::getInstance();
218
+		$certDir = $globalConfig->getPathValue('certdir', 'cert/');
219
+
220
+		$signKey = $config->getString('sign.privatekey', null);
221
+		if ($signKey !== null) {
222
+			$signKey = System::resolvePath($signKey, $certDir);
223
+			$this->signKey = @file_get_contents($signKey);
224
+			if ($this->signKey === null) {
225
+				throw new Exception('Unable to load private key from '.var_export($signKey, true));
226
+			}
227
+		}
228
+
229
+		$this->signKeyPass = $config->getString('sign.privatekey_pass', null);
230
+
231
+		$signCert = $config->getString('sign.certificate', null);
232
+		if ($signCert !== null) {
233
+			$signCert = System::resolvePath($signCert, $certDir);
234
+			$this->signCert = @file_get_contents($signCert);
235
+			if ($this->signCert === null) {
236
+				throw new Exception('Unable to load certificate file from '.var_export($signCert, true));
237
+			}
238
+		}
239
+
240
+		$this->signAlg = $config->getString('sign.algorithm', XMLSecurityKey::RSA_SHA1);
241
+		if (!in_array($this->signAlg, self::$SUPPORTED_SIGNATURE_ALGORITHMS)) {
242
+			throw new Exception('Unsupported signature algorithm '.var_export($this->signAlg, true));
243
+		}
244
+
245
+		$this->sslCAFile = $config->getString('ssl.cafile', null);
246
+
247
+		$this->regInfo = $config->getArray('RegistrationInfo', null);
248
+
249
+		$this->initSources($config->getConfigList('sources'));
250
+	}
251
+
252
+
253
+	/**
254
+	 * Populate the sources array.
255
+	 *
256
+	 * This is called from the constructor, and can be overridden in subclasses.
257
+	 *
258
+	 * @param array $sources  The sources as an array of SimpleSAML_Configuration objects.
259
+	 */
260
+	protected function initSources(array $sources)
261
+	{
262
+		foreach ($sources as $source) {
263
+			$this->sources[] = new EntitySource($this, $source);
264
+		}
265
+	}
266
+
267
+
268
+	/**
269
+	 * Return an instance of the aggregator with the given id.
270
+	 *
271
+	 * @param string $id  The id of the aggregator.
272
+	 */
273
+	public static function getAggregator($id)
274
+	{
275
+		assert('is_string($id)');
276
+
277
+		$config = Configuration::getConfig('module_aggregator2.php');
278
+		return new Aggregator($id, $config->getConfigItem($id));
279
+	}
280
+
281
+
282
+	/**
283
+	 * Retrieve the ID of the aggregator.
284
+	 *
285
+	 * @return string  The ID of this aggregator.
286
+	 */
287
+	public function getId()
288
+	{
289
+		return $this->id;
290
+	}
291
+
292
+
293
+	/**
294
+	 * Add an item to the cache.
295
+	 *
296
+	 * @param string $id  The identifier of this data.
297
+	 * @param string $data  The data.
298
+	 * @param int $expires  The timestamp the data expires.
299
+	 * @param string|null $tag  An extra tag that can be used to verify the validity of the cached data.
300
+	 */
301
+	public function addCacheItem($id, $data, $expires, $tag = null)
302
+	{
303
+		assert('is_string($id)');
304
+		assert('is_string($data)');
305
+		assert('is_int($expires)');
306
+		assert('is_null($tag) || is_string($tag)');
307
+
308
+		$cacheFile = $this->cacheDirectory.'/'.$id;
309
+		try {
310
+			System::writeFile($cacheFile, $data);
311
+		} catch (\Exception $e) {
312
+			Logger::warning($this->logLoc.'Unable to write to cache file '.var_export($cacheFile, true));
313
+			return;
314
+		}
315
+
316
+		$expireInfo = (string)$expires;
317
+		if ($tag !== null) {
318
+			$expireInfo .= ':'.$tag;
319
+		}
320
+
321
+		$expireFile = $cacheFile.'.expire';
322
+		try {
323
+			System::writeFile($expireFile, $expireInfo);
324
+		} catch (\Exception $e) {
325
+			Logger::warning($this->logLoc.'Unable to write expiration info to '.var_export($expireFile, true));
326
+		}
327
+	}
328
+
329
+
330
+	/**
331
+	 * Check validity of cached data.
332
+	 *
333
+	 * @param string $id  The identifier of this data.
334
+	 * @param string $tag  The tag that was passed to addCacheItem.
335
+	 * @return bool  TRUE if the data is valid, FALSE if not.
336
+	 */
337
+	public function isCacheValid($id, $tag = null)
338
+	{
339
+		assert('is_string($id)');
340
+		assert('is_null($tag) || is_string($tag)');
341
+
342
+		$cacheFile = $this->cacheDirectory.'/'.$id;
343
+		if (!file_exists($cacheFile)) {
344
+			return false;
345
+		}
346
+
347
+		$expireFile = $cacheFile.'.expire';
348
+		if (!file_exists($expireFile)) {
349
+			return false;
350
+		}
351
+
352
+		$expireData = @file_get_contents($expireFile);
353
+		if ($expireData === false) {
354
+			return false;
355
+		}
356
+
357
+		$expireData = explode(':', $expireData, 2);
358
+
359
+		$expireTime = intval($expireData[0]);
360
+		if ($expireTime <= time()) {
361
+			return false;
362
+		}
363
+
364
+		if (count($expireData) === 1) {
365
+			$expireTag = null;
366
+		} else {
367
+			$expireTag = $expireData[1];
368
+		}
369
+		if ($expireTag !== $tag) {
370
+			return false;
371
+		}
372
+
373
+		return true;
374
+	}
375
+
376
+
377
+	/**
378
+	 * Get the cache item.
379
+	 *
380
+	 * @param string $id  The identifier of this data.
381
+	 * @param string $tag  The tag that was passed to addCacheItem.
382
+	 * @return string|null  The cache item, or NULL if it isn't cached or if it is expired.
383
+	 */
384
+	public function getCacheItem($id, $tag = null)
385
+	{
386
+		assert('is_string($id)');
387
+		assert('is_null($tag) || is_string($tag)');
388
+
389
+		if (!$this->isCacheValid($id, $tag)) {
390
+			return null;
391
+		}
392
+
393
+		$cacheFile = $this->cacheDirectory.'/'.$id;
394
+		return @file_get_contents($cacheFile);
395
+	}
396
+
397
+
398
+	/**
399
+	 * Get the cache filename for the specific id.
400
+	 *
401
+	 * @param string $id  The identifier of the cached data.
402
+	 * @return string|null  The filename, or NULL if the cache file doesn't exist.
403
+	 */
404
+	public function getCacheFile($id)
405
+	{
406
+		assert('is_string($id)');
407
+
408
+		$cacheFile = $this->cacheDirectory.'/'.$id;
409
+		if (!file_exists($cacheFile)) {
410
+			return null;
411
+		}
412
+
413
+		return $cacheFile;
414
+	}
415
+
416
+
417
+	/**
418
+	 * Retrieve the SSL CA file path, if it is set.
419
+	 *
420
+	 * @return string|null  The SSL CA file path.
421
+	 */
422
+	public function getCAFile()
423
+	{
424
+		return $this->sslCAFile;
425
+	}
426
+
427
+
428
+	/**
429
+	 * Sign the generated EntitiesDescriptor.
430
+	 */
431
+	protected function addSignature(SignedElement $element)
432
+	{
433
+		if ($this->signKey === null) {
434
+			return;
435
+		}
436
+
437
+		$privateKey = new XMLSecurityKey($this->signAlg, ['type' => 'private']);
438
+		if ($this->signKeyPass !== null) {
439
+			$privateKey->passphrase = $this->signKeyPass;
440
+		}
441
+		$privateKey->loadKey($this->signKey, false);
442
+
443
+		$element->setSignatureKey($privateKey);
444
+
445
+		if ($this->signCert !== null) {
446
+			$element->setCertificates([$this->signCert]);
447
+		}
448
+	}
449
+
450
+
451
+	/**
452
+	 * Recursively browse the children of an EntitiesDescriptor element looking for EntityDescriptor elements, and
453
+	 * return an array containing all of them.
454
+	 *
455
+	 * @param \SAML2\XML\md\EntitiesDescriptor $entity The source EntitiesDescriptor that holds the entities to extract.
456
+	 *
457
+	 * @return array An array containing all the EntityDescriptors found.
458
+	 */
459
+	private static function extractEntityDescriptors($entity)
460
+	{
461
+		assert('$entity instanceof EntitiesDescriptor');
462
+
463
+		if (!($entity instanceof EntitiesDescriptor)) {
464
+			return [];
465
+		}
466
+
467
+		$results = [];
468
+		foreach ($entity->children as $child) {
469
+			if ($child instanceof EntityDescriptor) {
470
+				$results[] = $child;
471
+				continue;
472
+			}
473
+
474
+			$results = array_merge($results, self::extractEntityDescriptors($child));
475
+		}
476
+		return $results;
477
+	}
478
+
479
+
480
+	/**
481
+	 * Retrieve all entities as an EntitiesDescriptor.
482
+	 *
483
+	 * @return \SAML2\XML\md\EntitiesDescriptor  The entities.
484
+	 */
485
+	protected function getEntitiesDescriptor()
486
+	{
487
+		$ret = new EntitiesDescriptor();
488
+		$now = time();
489
+
490
+		// add RegistrationInfo extension if enabled
491
+		if ($this->regInfo !== null) {
492
+			$ri = new RegistrationInfo();
493
+			$ri->registrationInstant = $now;
494
+			foreach ($this->regInfo as $riName => $riValues) {
495
+				switch ($riName) {
496
+					case 'authority':
497
+						$ri->registrationAuthority = $riValues;
498
+						break;
499
+					case 'instant':
500
+						$ri->registrationInstant = Utils::xsDateTimeToTimestamp($riValues);
501
+						break;
502
+					case 'policies':
503
+						$ri->RegistrationPolicy = $riValues;
504
+						break;
505
+				}
506
+			}
507
+			$ret->Extensions[] = $ri;
508
+		}
509
+
510
+		foreach ($this->sources as $source) {
511
+			$m = $source->getMetadata();
512
+			if ($m === NULL) {
513
+				continue;
514
+			}
515
+			if ($m instanceof EntityDescriptor) {
516
+				$ret->children[] = $m;
517
+			} elseif ($m instanceof EntitiesDescriptor) {
518
+				$ret->children = array_merge($ret->children, self::extractEntityDescriptors($m));
519
+			}
520
+		}
521
+
522
+		$ret->children = array_unique($ret->children, SORT_REGULAR);
523
+		$ret->validUntil = $now + $this->validLength;
524
+
525
+		return $ret;
526
+	}
527
+
528
+
529
+	/**
530
+	 * Recursively traverse the children of an EntitiesDescriptor, removing those entities listed in the $entities
531
+	 * property. Returns the EntitiesDescriptor with the entities filtered out.
532
+	 *
533
+	 * @param \SAML2\XML\md\EntitiesDescriptor $descriptor The EntitiesDescriptor from where to exclude entities.
534
+	 *
535
+	 * @return \SAML2\XML\md\EntitiesDescriptor The EntitiesDescriptor with excluded entities filtered out.
536
+	 */
537
+	protected function exclude(EntitiesDescriptor $descriptor)
538
+	{
539
+		if (empty($this->excluded)) {
540
+			return $descriptor;
541
+		}
542
+
543
+		$filtered = [];
544
+		foreach ($descriptor->children as $child) {
545
+			if ($child instanceof EntityDescriptor) {
546
+				if (in_array($child->entityID, $this->excluded)) {
547
+					continue;
548
+				}
549
+				$filtered[] = $child;
550
+			}
551
+
552
+			if ($child instanceof EntitiesDescriptor) {
553
+				$filtered[] = $this->exclude($child);
554
+			}
555
+		}
556
+
557
+		$descriptor->children = $filtered;
558
+		return $descriptor;
559
+	}
560
+
561
+
562
+	/**
563
+	 * Recursively traverse the children of an EntitiesDescriptor, keeping only those entities with the roles listed in
564
+	 * the $roles property, and support for the protocols listed in the $protocols property. Returns the
565
+	 * EntitiesDescriptor containing only those entities.
566
+	 *
567
+	 * @param \SAML2\XML\md\EntitiesDescriptor $descriptor The EntitiesDescriptor to filter.
568
+	 *
569
+	 * @return SAML2_XML_md_EntitiesDescriptor The EntitiesDescriptor with only the entities filtered.
570
+	 */
571
+	protected function filter(EntitiesDescriptor $descriptor)
572
+	{
573
+		if ($this->roles === null || $this->protocols === null) {
574
+			return $descriptor;
575
+		}
576
+
577
+		$enabled_roles = array_keys($this->roles, true);
578
+		$enabled_protos = array_keys($this->protocols, true);
579
+
580
+		$filtered = [];
581
+		foreach ($descriptor->children as $child) {
582
+			if ($child instanceof EntityDescriptor) {
583
+				foreach ($child->RoleDescriptor as $role) {
584
+					if (in_array(get_class($role), $enabled_roles)) {
585
+						// we found a role descriptor that is enabled by our filters, check protocols
586
+						if (array_intersect($enabled_protos, $role->protocolSupportEnumeration) !== []) {
587
+							// it supports some protocol we have enabled, add it
588
+							$filtered[] = $child;
589
+							break;
590
+						}
591
+					}
592
+				}
593
+
594
+			}
595
+
596
+			if ($child instanceof EntitiesDescriptor) {
597
+				$filtered[] = $this->filter($child);
598
+			}
599
+		}
600
+
601
+		$descriptor->children = $filtered;
602
+		return $descriptor;
603
+	}
604
+
605
+
606
+	/**
607
+	 * Set this aggregator to exclude a set of entities from the resulting aggregate.
608
+	 *
609
+	 * @param array|null $entities The entity IDs of the entities to exclude.
610
+	 */
611
+	public function excludeEntities($entities)
612
+	{
613
+		assert('is_array($entities) || is_null($entities)');
614
+
615
+		if ($entities === null) {
616
+			return;
617
+		}
618
+		$this->excluded = $entities;
619
+		sort($this->excluded);
620
+		$this->cacheId = sha1($this->cacheId.serialize($this->excluded));
621
+	}
622
+
623
+
624
+	/**
625
+	 * Set the internal filters according to one or more options:
626
+	 *
627
+	 * - 'saml2': all SAML2.0-capable entities.
628
+	 * - 'shib13': all SHIB1.3-capable entities.
629
+	 * - 'saml20-idp': all SAML2.0-capable identity providers.
630
+	 * - 'saml20-sp': all SAML2.0-capable service providers.
631
+	 * - 'saml20-aa': all SAML2.0-capable attribute authorities.
632
+	 * - 'shib13-idp': all SHIB1.3-capable identity providers.
633
+	 * - 'shib13-sp': all SHIB1.3-capable service providers.
634
+	 * - 'shib13-aa': all SHIB1.3-capable attribute authorities.
635
+	 *
636
+	 * @param array|null $set An array of the different roles and protocols to filter by.
637
+	 */
638
+	public function setFilters($set)
639
+	{
640
+		assert('is_array($set) || is_null($set)');
641
+
642
+		if ($set === null) {
643
+			return;
644
+		}
645
+
646
+		// configure filters
647
+		$this->protocols = [
648
+			Constants::NS_SAMLP                    => true,
649
+			'urn:oasis:names:tc:SAML:1.1:protocol' => true,
650
+		];
651
+		$this->roles = [
652
+			'SAML2_XML_md_IDPSSODescriptor'             => true,
653
+			'SAML2_XML_md_SPSSODescriptor'              => true,
654
+			'SAML2_XML_md_AttributeAuthorityDescriptor' => true,
655
+		];
656
+
657
+		// now translate from the options we have, to specific protocols and roles
658
+
659
+		// check SAML 2.0 protocol
660
+		$options = ['saml2', 'saml20-idp', 'saml20-sp', 'saml20-aa'];
661
+		$this->protocols[Constants::NS_SAMLP] = (array_intersect($set, $options) !== []);
662
+
663
+		// check SHIB 1.3 protocol
664
+		$options = ['shib13', 'shib13-idp', 'shib13-sp', 'shib13-aa'];
665
+		$this->protocols['urn:oasis:names:tc:SAML:1.1:protocol'] = (array_intersect($set, $options) !== []);
666
+
667
+		// check IdP
668
+		$options = ['saml2', 'shib13', 'saml20-idp', 'shib13-idp'];
669
+		$this->roles['SAML2_XML_md_IDPSSODescriptor'] = (array_intersect($set, $options) !== []);
670
+
671
+		// check SP
672
+		$options = ['saml2', 'shib13', 'saml20-sp', 'shib13-sp'];
673
+		$this->roles['SAML2_XML_md_SPSSODescriptor'] = (array_intersect($set, $options) !== []);
674
+
675
+		// check AA
676
+		$options = ['saml2', 'shib13', 'saml20-aa', 'shib13-aa'];
677
+		$this->roles['SAML2_XML_md_AttributeAuthorityDescriptor'] = (array_intersect($set, $options) !== []);
678
+
679
+		$this->cacheId = sha1($this->cacheId.serialize($this->protocols).serialize($this->roles));
680
+	}
681
+
682
+
683
+	/**
684
+	 * Retrieve the complete, signed metadata as text.
685
+	 *
686
+	 * This function will write the new metadata to the cache file, but will not return
687
+	 * the cached metadata.
688
+	 *
689
+	 * @return string  The metadata, as text.
690
+	 */
691
+	public function updateCachedMetadata()
692
+	{
693
+		$ed = $this->getEntitiesDescriptor();
694
+		$ed = $this->exclude($ed);
695
+		$ed = $this->filter($ed);
696
+		$this->addSignature($ed);
697
+
698
+		$xml = $ed->toXML();
699
+		$xml = $xml->ownerDocument->saveXML($xml);
700
+
701
+		if ($this->cacheGenerated !== null) {
702
+			Logger::debug($this->logLoc.'Saving generated metadata to cache.');
703
+			$this->addCacheItem($this->cacheId, $xml, time() + $this->cacheGenerated, $this->cacheTag);
704
+		}
705
+
706
+		return $xml;
707
+	}
708
+
709
+
710
+	/**
711
+	 * Retrieve the complete, signed metadata as text.
712
+	 *
713
+	 * @return string  The metadata, as text.
714
+	 */
715
+	public function getMetadata()
716
+	{
717
+		if ($this->cacheGenerated !== null) {
718
+			$xml = $this->getCacheItem($this->cacheId, $this->cacheTag);
719
+			if ($xml !== null) {
720
+				Logger::debug($this->logLoc.'Loaded generated metadata from cache.');
721
+				return $xml;
722
+			}
723
+		}
724
+
725
+		return $this->updateCachedMetadata();
726
+	}
727
+
728
+
729
+	/**
730
+	 * Update the cached copy of our metadata.
731
+	 */
732
+	public function updateCache()
733
+	{
734
+		foreach ($this->sources as $source) {
735
+			$source->updateCache();
736
+		}
737
+
738
+		$this->updateCachedMetadata();
739
+	}
740 740
 }
Please login to merge, or discard this patch.
templates/list.php 2 patches
Indentation   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -6,29 +6,29 @@
 block discarded – undo
6 6
 
7 7
 <?php
8 8
 if (count($this->data['sources']) === 0) {
9
-    echo "    <p>".$this->t('{aggregator2:aggregator:no_aggregators}')."</p>\n";
9
+	echo "    <p>".$this->t('{aggregator2:aggregator:no_aggregators}')."</p>\n";
10 10
 } else {
11
-    echo "    <ul>";
11
+	echo "    <ul>";
12 12
 
13
-    foreach ($this->data['sources'] as $id => $source) {
14
-        $encId = urlencode($id);
15
-        $params = [
16
-            'id' => $encId,
17
-        ];
18
-        echo str_repeat(' ', 8)."<li>\n";
19
-        echo str_repeat(' ', 12).'<a href="';
20
-        echo SimpleSAML\Module::getModuleURL('aggregator2/get.php', $params).'">'.htmlspecialchars($id)."</a>\n";
21
-        echo str_repeat(' ', 12).'<a href="';
22
-        $params['mimetype'] = 'text/plain';
23
-        echo SimpleSAML\Module::getModuleURL('aggregator2/get.php', $params).'">['.
24
-            $this->t('{aggregator2:aggregator:text}')."]</a>\n";
25
-        echo str_repeat(' ', 12).'<a href="';
26
-        $params['mimetype'] = 'application/xml';
27
-        echo SimpleSAML\Module::getModuleURL('aggregator2/get.php', $params)."\">[XML]</a>\n";
28
-        echo str_repeat(' ', 8)."</li>\n";
29
-    }
13
+	foreach ($this->data['sources'] as $id => $source) {
14
+		$encId = urlencode($id);
15
+		$params = [
16
+			'id' => $encId,
17
+		];
18
+		echo str_repeat(' ', 8)."<li>\n";
19
+		echo str_repeat(' ', 12).'<a href="';
20
+		echo SimpleSAML\Module::getModuleURL('aggregator2/get.php', $params).'">'.htmlspecialchars($id)."</a>\n";
21
+		echo str_repeat(' ', 12).'<a href="';
22
+		$params['mimetype'] = 'text/plain';
23
+		echo SimpleSAML\Module::getModuleURL('aggregator2/get.php', $params).'">['.
24
+			$this->t('{aggregator2:aggregator:text}')."]</a>\n";
25
+		echo str_repeat(' ', 12).'<a href="';
26
+		$params['mimetype'] = 'application/xml';
27
+		echo SimpleSAML\Module::getModuleURL('aggregator2/get.php', $params)."\">[XML]</a>\n";
28
+		echo str_repeat(' ', 8)."</li>\n";
29
+	}
30 30
 
31
-    echo "    </ul>\n";
31
+	echo "    </ul>\n";
32 32
 }
33 33
 
34 34
 $this->includeAtTemplateBase('includes/footer.php');
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -6,7 +6,7 @@  discard block
 block discarded – undo
6 6
 
7 7
 <?php
8 8
 if (count($this->data['sources']) === 0) {
9
-    echo "    <p>".$this->t('{aggregator2:aggregator:no_aggregators}')."</p>\n";
9
+    echo "    <p>" . $this->t('{aggregator2:aggregator:no_aggregators}') . "</p>\n";
10 10
 } else {
11 11
     echo "    <ul>";
12 12
 
@@ -15,17 +15,17 @@  discard block
 block discarded – undo
15 15
         $params = [
16 16
             'id' => $encId,
17 17
         ];
18
-        echo str_repeat(' ', 8)."<li>\n";
19
-        echo str_repeat(' ', 12).'<a href="';
20
-        echo SimpleSAML\Module::getModuleURL('aggregator2/get.php', $params).'">'.htmlspecialchars($id)."</a>\n";
21
-        echo str_repeat(' ', 12).'<a href="';
18
+        echo str_repeat(' ', 8) . "<li>\n";
19
+        echo str_repeat(' ', 12) . '<a href="';
20
+        echo SimpleSAML\Module::getModuleURL('aggregator2/get.php', $params) . '">' . htmlspecialchars($id) . "</a>\n";
21
+        echo str_repeat(' ', 12) . '<a href="';
22 22
         $params['mimetype'] = 'text/plain';
23
-        echo SimpleSAML\Module::getModuleURL('aggregator2/get.php', $params).'">['.
24
-            $this->t('{aggregator2:aggregator:text}')."]</a>\n";
25
-        echo str_repeat(' ', 12).'<a href="';
23
+        echo SimpleSAML\Module::getModuleURL('aggregator2/get.php', $params) . '">[' .
24
+            $this->t('{aggregator2:aggregator:text}') . "]</a>\n";
25
+        echo str_repeat(' ', 12) . '<a href="';
26 26
         $params['mimetype'] = 'application/xml';
27
-        echo SimpleSAML\Module::getModuleURL('aggregator2/get.php', $params)."\">[XML]</a>\n";
28
-        echo str_repeat(' ', 8)."</li>\n";
27
+        echo SimpleSAML\Module::getModuleURL('aggregator2/get.php', $params) . "\">[XML]</a>\n";
28
+        echo str_repeat(' ', 8) . "</li>\n";
29 29
     }
30 30
 
31 31
     echo "    </ul>\n";
Please login to merge, or discard this patch.
config-templates/module_aggregator2.php 1 patch
Indentation   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -2,35 +2,35 @@  discard block
 block discarded – undo
2 2
 
3 3
 // This is the configuration file for the aggregator2-module
4 4
 $config = [
5
-    /*
5
+	/*
6 6
      * 'example' will be one set of aggregated metadata.
7 7
      * The aggregated metadata can be retrieved from:
8 8
      *   https://.../simplesaml/module.php/aggregator2/get.php?id=example
9 9
      */
10
-    'example' => [
11
-        // 'sources' is an array with the places we want to fetch metadata from
12
-        'sources' => [
13
-            /* Metadata validated by the https-certificate of the server. */
14
-            [
15
-                /* The URL we should fetch the metadata from. */
16
-                'url' => 'https://sp.example.org/metadata.xml',
10
+	'example' => [
11
+		// 'sources' is an array with the places we want to fetch metadata from
12
+		'sources' => [
13
+			/* Metadata validated by the https-certificate of the server. */
14
+			[
15
+				/* The URL we should fetch the metadata from. */
16
+				'url' => 'https://sp.example.org/metadata.xml',
17 17
 
18
-                /*
18
+				/*
19 19
                  * To enable validation of the https-certificate, we must
20 20
                  * specify a file with valid CA certificates.
21 21
                  *
22 22
                  * This can be an absolute path, or a path relative to the
23 23
                  * cert-directory.
24 24
                  */
25
-                'ssl.cafile' => '/etc/ssl/certs/ca-certificates.crt',
26
-            ],
25
+				'ssl.cafile' => '/etc/ssl/certs/ca-certificates.crt',
26
+			],
27 27
 
28
-            /* Metadata validated by its signature. */
29
-            [
30
-                /* The URL we should fetch the metadata from. */
31
-                'url' => 'http://idp.example.org/metadata.xml',
28
+			/* Metadata validated by its signature. */
29
+			[
30
+				/* The URL we should fetch the metadata from. */
31
+				'url' => 'http://idp.example.org/metadata.xml',
32 32
 
33
-                /*
33
+				/*
34 34
                  * To verify the signature in the metadata, we must specify
35 35
                  * a certificate that should be used. Note: This cannot
36 36
                  * be a CA certificate.
@@ -38,17 +38,17 @@  discard block
 block discarded – undo
38 38
                  * This can be an absolute path, or a path relative to the
39 39
                  * cert-directory.
40 40
                  */
41
-                'cert' => 'idp.example.org.crt',
42
-            ],
41
+				'cert' => 'idp.example.org.crt',
42
+			],
43 43
 
44
-            /* Metadata from a file. */
45
-            [
46
-                'url' => '/var/simplesaml/somemetadata.xml',
47
-            ],
44
+			/* Metadata from a file. */
45
+			[
46
+				'url' => '/var/simplesaml/somemetadata.xml',
47
+			],
48 48
 
49
-        ],
49
+		],
50 50
 
51
-        /*
51
+		/*
52 52
          * Update this metadata during this cron tag.
53 53
          *
54 54
          * For this option to work, you must configure the cron-module,
@@ -57,9 +57,9 @@  discard block
 block discarded – undo
57 57
          * This option is optional. If cron is not configured, the metadata
58 58
          * caches will be updated when receiving requests for metadata.
59 59
          */
60
-        'cron.tag' => 'hourly',
60
+		'cron.tag' => 'hourly',
61 61
 
62
-        /*
62
+		/*
63 63
          * The directory we will store downloaded and generated metadata.
64 64
          * This directory must be writeable by the web-server.
65 65
          *
@@ -67,9 +67,9 @@  discard block
 block discarded – undo
67 67
          * aggregated metadata will result in the aggregator fetching and
68 68
          * parsing all metadata sources.
69 69
          */
70
-        'cache.directory' => '/var/cache/simplesaml-aggregator2',
70
+		'cache.directory' => '/var/cache/simplesaml-aggregator2',
71 71
 
72
-        /*
72
+		/*
73 73
          * This is the number of seconds we will cache the metadata file we generate.
74 74
          * This should be a longer time than the interval between each time the cron
75 75
          * job is executed.
@@ -77,34 +77,34 @@  discard block
 block discarded – undo
77 77
          * This option is optional. If unspecified, the metadata will be generated
78 78
          * on every request.
79 79
          */
80
-        'cache.generated' => 24*60*60,
80
+		'cache.generated' => 24*60*60,
81 81
 
82
-        /*
82
+		/*
83 83
          * The generated metadata will have a validUntil set to the time it is generated
84 84
          * plus this number of seconds.
85 85
          */
86
-        'valid.length' => 7*24*60*60,
86
+		'valid.length' => 7*24*60*60,
87 87
 
88
-        /*
88
+		/*
89 89
          * The private key we should use to sign the metadata, in pem-format.
90 90
          *
91 91
          * This is optional. If it is not specified, the metadata will not be signed.
92 92
          */
93
-        'sign.privatekey' => 'metadata.pem',
93
+		'sign.privatekey' => 'metadata.pem',
94 94
 
95
-        /*
95
+		/*
96 96
          * The password for the private key.
97 97
          *
98 98
          * Optional, the private key is assumed to be unencrypted if this option
99 99
          * isn't set.
100 100
          */
101
-        'sign.privatekey_pass' => 'secret',
101
+		'sign.privatekey_pass' => 'secret',
102 102
 
103
-        /*
103
+		/*
104 104
          * The certificate that corresponds to the private key.
105 105
          *
106 106
          * If specified, the certificate will be included in the signature in the metadata.
107 107
          */
108
-        'sign.certificate' => 'metadata.crt',
109
-    ],
108
+		'sign.certificate' => 'metadata.crt',
109
+	],
110 110
 ];
Please login to merge, or discard this patch.
tests/bootstrap.php 2 patches
Indentation   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -6,7 +6,7 @@
 block discarded – undo
6 6
 // Symlink module into ssp vendor lib so that templates and urls can resolve correctly
7 7
 $linkPath = $projectRoot.'/vendor/simplesamlphp/simplesamlphp/modules/aggregator2';
8 8
 if (file_exists($linkPath) === false) {
9
-    echo "Linking '$linkPath' to '$projectRoot'\n";
10
-    symlink($projectRoot, $linkPath);
9
+	echo "Linking '$linkPath' to '$projectRoot'\n";
10
+	symlink($projectRoot, $linkPath);
11 11
 }
12 12
 
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -1,10 +1,10 @@
 block discarded – undo
1 1
 <?php
2 2
 
3 3
 $projectRoot = dirname(__DIR__);
4
-require_once($projectRoot.'/vendor/autoload.php');
4
+require_once($projectRoot . '/vendor/autoload.php');
5 5
 
6 6
 // Symlink module into ssp vendor lib so that templates and urls can resolve correctly
7
-$linkPath = $projectRoot.'/vendor/simplesamlphp/simplesamlphp/modules/aggregator2';
7
+$linkPath = $projectRoot . '/vendor/simplesamlphp/simplesamlphp/modules/aggregator2';
8 8
 if (file_exists($linkPath) === false) {
9 9
     echo "Linking '$linkPath' to '$projectRoot'\n";
10 10
     symlink($projectRoot, $linkPath);
Please login to merge, or discard this patch.
hooks/hook_cron.php 1 patch
Indentation   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -7,28 +7,28 @@
 block discarded – undo
7 7
  */
8 8
 function aggregator2_hook_cron(&$croninfo)
9 9
 {
10
-    assert('is_array($croninfo)');
11
-    assert('array_key_exists("summary", $croninfo)');
12
-    assert('array_key_exists("tag", $croninfo)');
10
+	assert('is_array($croninfo)');
11
+	assert('array_key_exists("summary", $croninfo)');
12
+	assert('array_key_exists("tag", $croninfo)');
13 13
 
14
-    $cronTag = $croninfo['tag'];
14
+	$cronTag = $croninfo['tag'];
15 15
 
16
-    $config = \SimpleSAML\Configuration::getConfig('module_aggregator2.php');
17
-    $config = $config->toArray();
16
+	$config = \SimpleSAML\Configuration::getConfig('module_aggregator2.php');
17
+	$config = $config->toArray();
18 18
 
19
-    foreach ($config as $id => $c) {
20
-        if (!isset($c['cron.tag'])) {
21
-            continue;
22
-        }
23
-        if ($c['cron.tag'] !== $cronTag) {
24
-            continue;
25
-        }
19
+	foreach ($config as $id => $c) {
20
+		if (!isset($c['cron.tag'])) {
21
+			continue;
22
+		}
23
+		if ($c['cron.tag'] !== $cronTag) {
24
+			continue;
25
+		}
26 26
 
27
-        try {
28
-            $a = \SimpleSAML\Module\aggregator2\Aggregator::getAggregator($id);
29
-            $a->updateCache();
30
-        } catch (\Exception $e) {
31
-            $croninfo['summary'][] = 'Error during aggregator2 cacheupdate: ' . $e->getMessage();
32
-        }
33
-    }
27
+		try {
28
+			$a = \SimpleSAML\Module\aggregator2\Aggregator::getAggregator($id);
29
+			$a->updateCache();
30
+		} catch (\Exception $e) {
31
+			$croninfo['summary'][] = 'Error during aggregator2 cacheupdate: ' . $e->getMessage();
32
+		}
33
+	}
34 34
 }
Please login to merge, or discard this patch.
hooks/hook_frontpage.php 1 patch
Indentation   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -7,11 +7,11 @@
 block discarded – undo
7 7
  */
8 8
 function aggregator2_hook_frontpage(&$links)
9 9
 {
10
-    assert('is_array($links)');
11
-    assert('array_key_exists("links", $links)');
10
+	assert('is_array($links)');
11
+	assert('array_key_exists("links", $links)');
12 12
 
13
-    $links['federation'][] = [
14
-        'href' => \SimpleSAML\Module::getModuleURL('aggregator2/'),
15
-        'text' => '{aggregator2:aggregator:frontpage_link}',
16
-    ];
13
+	$links['federation'][] = [
14
+		'href' => \SimpleSAML\Module::getModuleURL('aggregator2/'),
15
+		'text' => '{aggregator2:aggregator:frontpage_link}',
16
+	];
17 17
 }
Please login to merge, or discard this patch.
lib/EntitySource.php 2 patches
Indentation   +233 added lines, -233 removed lines patch added patch discarded remove patch
@@ -21,237 +21,237 @@
 block discarded – undo
21 21
  */
22 22
 class EntitySource
23 23
 {
24
-    /**
25
-     * Our log "location".
26
-     *
27
-     * @var string
28
-     */
29
-    protected $logLoc;
30
-
31
-    /**
32
-     * The aggregator we belong to.
33
-     *
34
-     * @var \SimpleSAML\Module\aggregator2\Aggregator
35
-     */
36
-    protected $aggregator;
37
-
38
-    /**
39
-     * The URL we should fetch it from.
40
-     *
41
-     * @var string
42
-     */
43
-    protected $url;
44
-
45
-    /**
46
-     * The SSL CA file that should be used to validate the connection.
47
-     *
48
-     * @var string|null
49
-     */
50
-    protected $sslCAFile;
51
-
52
-    /**
53
-     * The certificate we should use to validate downloaded metadata.
54
-     *
55
-     * @var string|null
56
-     */
57
-    protected $certificate;
58
-
59
-    /**
60
-     * The parsed metadata.
61
-     *
62
-     * @var \SAML2\XML\md\EntitiesDescriptor|\SAML2\XML\md\EntityDescriptor|null
63
-     */
64
-    protected $metadata;
65
-
66
-    /**
67
-     * The cache ID.
68
-     *
69
-     * @var string
70
-     */
71
-    protected $cacheId;
72
-
73
-    /**
74
-     * The cache tag.
75
-     *
76
-     * @var string
77
-     */
78
-    protected $cacheTag;
79
-
80
-    /**
81
-     * Whether we have attempted to update the cache already.
82
-     *
83
-     * @var bool
84
-     */
85
-    protected $updateAttempted;
86
-
87
-
88
-    /**
89
-     * Initialize this EntitySource.
90
-     *
91
-     * @param \SimpleSAML\Configuration $config  The configuration.
92
-     */
93
-    public function __construct(Aggregator $aggregator, Configuration $config)
94
-    {
95
-        $this->logLoc = 'aggregator2:'.$aggregator->getId().': ';
96
-        $this->aggregator = $aggregator;
97
-
98
-        $this->url = $config->getString('url');
99
-        $this->sslCAFile = $config->getString('ssl.cafile', null);
100
-        if ($this->sslCAFile === null) {
101
-            $this->sslCAFile = $aggregator->getCAFile();
102
-        }
103
-
104
-        $this->certificate = $config->getString('cert', null);
105
-
106
-        $this->cacheId = sha1($this->url);
107
-        $this->cacheTag = sha1(serialize($config));
108
-    }
109
-
110
-
111
-    /**
112
-     * Retrieve and parse the metadata.
113
-     *
114
-     * @return \SAML2\XML\md\EntitiesDescriptor|\SAML2\XML\md\EntityDescriptor|null
115
-     * The downloaded metadata or NULL if we were unable to download or parse it.
116
-     */
117
-    private function downloadMetadata()
118
-    {
119
-        Logger::debug($this->logLoc.'Downloading metadata from '.var_export($this->url, true));
120
-
121
-        $context = ['ssl' => []];
122
-        if ($this->sslCAFile !== null) {
123
-            $context['ssl']['cafile'] = Config::getCertPath($this->sslCAFile);
124
-            Logger::debug($this->logLoc.'Validating https connection against CA certificate(s) found in '.
125
-                var_export($context['ssl']['cafile'], true));
126
-            $context['ssl']['verify_peer'] = true;
127
-            $context['ssl']['CN_match'] = parse_url($this->url, PHP_URL_HOST);
128
-        }
129
-
130
-        $data = HTTP::fetch($this->url, $context);
131
-        if ($data === false || $data === null) {
132
-            Logger::error($this->logLoc.'Unable to load metadata from '.var_export($this->url, true));
133
-            return null;
134
-        }
135
-
136
-        $doc = new \DOMDocument();
137
-        $res = $doc->loadXML($data);
138
-        if (!$res) {
139
-            Logger::error($this->logLoc.'Error parsing XML from '.var_export($this->url, true));
140
-            return null;
141
-        }
142
-
143
-        $root = Utils::xpQuery($doc->firstChild, '/saml_metadata:EntityDescriptor|/saml_metadata:EntitiesDescriptor');
144
-        if (count($root) === 0) {
145
-            Logger::error($this->logLoc.'No <EntityDescriptor> or <EntitiesDescriptor> in metadata from '.
146
-                var_export($this->url, true));
147
-            return null;
148
-        }
149
-
150
-        if (count($root) > 1) {
151
-            Logger::error($this->logLoc.'More than one <EntityDescriptor> or <EntitiesDescriptor> in metadata from '.
152
-                var_export($this->url, true));
153
-            return null;
154
-        }
155
-
156
-        $root = $root[0];
157
-        try {
158
-            if ($root->localName === 'EntityDescriptor') {
159
-                $md = new EntityDescriptor($root);
160
-            } else {
161
-                $md = new EntitiesDescriptor($root);
162
-            }
163
-        } catch (\Exception $e) {
164
-            Logger::error($this->logLoc.'Unable to parse metadata from '.
165
-                var_export($this->url, true).': '.$e->getMessage());
166
-            return null;
167
-        }
168
-
169
-        if ($this->certificate !== null) {
170
-            $file = Config::getCertPath($this->certificate);
171
-            $certData = file_get_contents($file);
172
-            if ($certData === false) {
173
-                throw new Exception('Error loading certificate from '.var_export($file, true));
174
-            }
175
-
176
-            // Extract the public key from the certificate for validation
177
-            $key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, ['type'=>'public']);
178
-            $key->loadKey($file, true);
179
-
180
-            if (!$md->validate($key)) {
181
-                Logger::error($this->logLoc.'Error validating signature on metadata.');
182
-                return null;
183
-            }
184
-            Logger::debug($this->logLoc.'Validated signature on metadata from '.var_export($this->url, true));
185
-        }
186
-
187
-        return $md;
188
-    }
189
-
190
-
191
-    /**
192
-     * Attempt to update our cache file.
193
-     */
194
-    public function updateCache()
195
-    {
196
-        if ($this->updateAttempted) {
197
-            return;
198
-        }
199
-        $this->updateAttempted = true;
200
-
201
-        $this->metadata = $this->downloadMetadata();
202
-        if ($this->metadata === null) {
203
-            return;
204
-        }
205
-
206
-        $expires = time() + 24*60*60; // Default expires in one day
207
-
208
-        if ($this->metadata->validUntil !== null && $this->metadata->validUntil < $expires) {
209
-            $expires = $this->metadata->validUntil;
210
-        }
211
-
212
-        $metadataSerialized = serialize($this->metadata);
213
-
214
-        $this->aggregator->addCacheItem($this->cacheId, $metadataSerialized, $expires, $this->cacheTag);
215
-    }
216
-
217
-
218
-    /**
219
-     * Retrieve the metadata file.
220
-     *
221
-     * This function will check its cached copy, to see whether it can be used.
222
-     *
223
-     * @return \SAML2\XML\md\EntityDescriptor|\SAML2\XML\md\EntitiesDescriptor|null  The downloaded metadata.
224
-     */
225
-    public function getMetadata()
226
-    {
227
-        if ($this->metadata !== null) {
228
-            /* We have already downloaded the metdata. */
229
-            return $this->metadata;
230
-        }
231
-
232
-        if (!$this->aggregator->isCacheValid($this->cacheId, $this->cacheTag)) {
233
-            $this->updateCache();
234
-            if ($this->metadata !== null) {
235
-                return $this->metadata;
236
-            }
237
-            /* We were unable to update the cache - use cached metadata. */
238
-        }
239
-
240
-        $cacheFile = $this->aggregator->getCacheFile($this->cacheId);
241
-
242
-        if (!file_exists($cacheFile)) {
243
-            Logger::error($this->logLoc . 'No cached metadata available.');
244
-            return null;
245
-        }
246
-
247
-        Logger::debug($this->logLoc.'Using cached metadata from '.var_export($cacheFile, true));
248
-
249
-        $metadata = file_get_contents($cacheFile);
250
-        if ($metadata !== null) {
251
-            $this->metadata = unserialize($metadata);
252
-            return $this->metadata;
253
-        }
254
-
255
-        return null;
256
-    }
24
+	/**
25
+	 * Our log "location".
26
+	 *
27
+	 * @var string
28
+	 */
29
+	protected $logLoc;
30
+
31
+	/**
32
+	 * The aggregator we belong to.
33
+	 *
34
+	 * @var \SimpleSAML\Module\aggregator2\Aggregator
35
+	 */
36
+	protected $aggregator;
37
+
38
+	/**
39
+	 * The URL we should fetch it from.
40
+	 *
41
+	 * @var string
42
+	 */
43
+	protected $url;
44
+
45
+	/**
46
+	 * The SSL CA file that should be used to validate the connection.
47
+	 *
48
+	 * @var string|null
49
+	 */
50
+	protected $sslCAFile;
51
+
52
+	/**
53
+	 * The certificate we should use to validate downloaded metadata.
54
+	 *
55
+	 * @var string|null
56
+	 */
57
+	protected $certificate;
58
+
59
+	/**
60
+	 * The parsed metadata.
61
+	 *
62
+	 * @var \SAML2\XML\md\EntitiesDescriptor|\SAML2\XML\md\EntityDescriptor|null
63
+	 */
64
+	protected $metadata;
65
+
66
+	/**
67
+	 * The cache ID.
68
+	 *
69
+	 * @var string
70
+	 */
71
+	protected $cacheId;
72
+
73
+	/**
74
+	 * The cache tag.
75
+	 *
76
+	 * @var string
77
+	 */
78
+	protected $cacheTag;
79
+
80
+	/**
81
+	 * Whether we have attempted to update the cache already.
82
+	 *
83
+	 * @var bool
84
+	 */
85
+	protected $updateAttempted;
86
+
87
+
88
+	/**
89
+	 * Initialize this EntitySource.
90
+	 *
91
+	 * @param \SimpleSAML\Configuration $config  The configuration.
92
+	 */
93
+	public function __construct(Aggregator $aggregator, Configuration $config)
94
+	{
95
+		$this->logLoc = 'aggregator2:'.$aggregator->getId().': ';
96
+		$this->aggregator = $aggregator;
97
+
98
+		$this->url = $config->getString('url');
99
+		$this->sslCAFile = $config->getString('ssl.cafile', null);
100
+		if ($this->sslCAFile === null) {
101
+			$this->sslCAFile = $aggregator->getCAFile();
102
+		}
103
+
104
+		$this->certificate = $config->getString('cert', null);
105
+
106
+		$this->cacheId = sha1($this->url);
107
+		$this->cacheTag = sha1(serialize($config));
108
+	}
109
+
110
+
111
+	/**
112
+	 * Retrieve and parse the metadata.
113
+	 *
114
+	 * @return \SAML2\XML\md\EntitiesDescriptor|\SAML2\XML\md\EntityDescriptor|null
115
+	 * The downloaded metadata or NULL if we were unable to download or parse it.
116
+	 */
117
+	private function downloadMetadata()
118
+	{
119
+		Logger::debug($this->logLoc.'Downloading metadata from '.var_export($this->url, true));
120
+
121
+		$context = ['ssl' => []];
122
+		if ($this->sslCAFile !== null) {
123
+			$context['ssl']['cafile'] = Config::getCertPath($this->sslCAFile);
124
+			Logger::debug($this->logLoc.'Validating https connection against CA certificate(s) found in '.
125
+				var_export($context['ssl']['cafile'], true));
126
+			$context['ssl']['verify_peer'] = true;
127
+			$context['ssl']['CN_match'] = parse_url($this->url, PHP_URL_HOST);
128
+		}
129
+
130
+		$data = HTTP::fetch($this->url, $context);
131
+		if ($data === false || $data === null) {
132
+			Logger::error($this->logLoc.'Unable to load metadata from '.var_export($this->url, true));
133
+			return null;
134
+		}
135
+
136
+		$doc = new \DOMDocument();
137
+		$res = $doc->loadXML($data);
138
+		if (!$res) {
139
+			Logger::error($this->logLoc.'Error parsing XML from '.var_export($this->url, true));
140
+			return null;
141
+		}
142
+
143
+		$root = Utils::xpQuery($doc->firstChild, '/saml_metadata:EntityDescriptor|/saml_metadata:EntitiesDescriptor');
144
+		if (count($root) === 0) {
145
+			Logger::error($this->logLoc.'No <EntityDescriptor> or <EntitiesDescriptor> in metadata from '.
146
+				var_export($this->url, true));
147
+			return null;
148
+		}
149
+
150
+		if (count($root) > 1) {
151
+			Logger::error($this->logLoc.'More than one <EntityDescriptor> or <EntitiesDescriptor> in metadata from '.
152
+				var_export($this->url, true));
153
+			return null;
154
+		}
155
+
156
+		$root = $root[0];
157
+		try {
158
+			if ($root->localName === 'EntityDescriptor') {
159
+				$md = new EntityDescriptor($root);
160
+			} else {
161
+				$md = new EntitiesDescriptor($root);
162
+			}
163
+		} catch (\Exception $e) {
164
+			Logger::error($this->logLoc.'Unable to parse metadata from '.
165
+				var_export($this->url, true).': '.$e->getMessage());
166
+			return null;
167
+		}
168
+
169
+		if ($this->certificate !== null) {
170
+			$file = Config::getCertPath($this->certificate);
171
+			$certData = file_get_contents($file);
172
+			if ($certData === false) {
173
+				throw new Exception('Error loading certificate from '.var_export($file, true));
174
+			}
175
+
176
+			// Extract the public key from the certificate for validation
177
+			$key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, ['type'=>'public']);
178
+			$key->loadKey($file, true);
179
+
180
+			if (!$md->validate($key)) {
181
+				Logger::error($this->logLoc.'Error validating signature on metadata.');
182
+				return null;
183
+			}
184
+			Logger::debug($this->logLoc.'Validated signature on metadata from '.var_export($this->url, true));
185
+		}
186
+
187
+		return $md;
188
+	}
189
+
190
+
191
+	/**
192
+	 * Attempt to update our cache file.
193
+	 */
194
+	public function updateCache()
195
+	{
196
+		if ($this->updateAttempted) {
197
+			return;
198
+		}
199
+		$this->updateAttempted = true;
200
+
201
+		$this->metadata = $this->downloadMetadata();
202
+		if ($this->metadata === null) {
203
+			return;
204
+		}
205
+
206
+		$expires = time() + 24*60*60; // Default expires in one day
207
+
208
+		if ($this->metadata->validUntil !== null && $this->metadata->validUntil < $expires) {
209
+			$expires = $this->metadata->validUntil;
210
+		}
211
+
212
+		$metadataSerialized = serialize($this->metadata);
213
+
214
+		$this->aggregator->addCacheItem($this->cacheId, $metadataSerialized, $expires, $this->cacheTag);
215
+	}
216
+
217
+
218
+	/**
219
+	 * Retrieve the metadata file.
220
+	 *
221
+	 * This function will check its cached copy, to see whether it can be used.
222
+	 *
223
+	 * @return \SAML2\XML\md\EntityDescriptor|\SAML2\XML\md\EntitiesDescriptor|null  The downloaded metadata.
224
+	 */
225
+	public function getMetadata()
226
+	{
227
+		if ($this->metadata !== null) {
228
+			/* We have already downloaded the metdata. */
229
+			return $this->metadata;
230
+		}
231
+
232
+		if (!$this->aggregator->isCacheValid($this->cacheId, $this->cacheTag)) {
233
+			$this->updateCache();
234
+			if ($this->metadata !== null) {
235
+				return $this->metadata;
236
+			}
237
+			/* We were unable to update the cache - use cached metadata. */
238
+		}
239
+
240
+		$cacheFile = $this->aggregator->getCacheFile($this->cacheId);
241
+
242
+		if (!file_exists($cacheFile)) {
243
+			Logger::error($this->logLoc . 'No cached metadata available.');
244
+			return null;
245
+		}
246
+
247
+		Logger::debug($this->logLoc.'Using cached metadata from '.var_export($cacheFile, true));
248
+
249
+		$metadata = file_get_contents($cacheFile);
250
+		if ($metadata !== null) {
251
+			$this->metadata = unserialize($metadata);
252
+			return $this->metadata;
253
+		}
254
+
255
+		return null;
256
+	}
257 257
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -92,7 +92,7 @@  discard block
 block discarded – undo
92 92
      */
93 93
     public function __construct(Aggregator $aggregator, Configuration $config)
94 94
     {
95
-        $this->logLoc = 'aggregator2:'.$aggregator->getId().': ';
95
+        $this->logLoc = 'aggregator2:' . $aggregator->getId() . ': ';
96 96
         $this->aggregator = $aggregator;
97 97
 
98 98
         $this->url = $config->getString('url');
@@ -116,12 +116,12 @@  discard block
 block discarded – undo
116 116
      */
117 117
     private function downloadMetadata()
118 118
     {
119
-        Logger::debug($this->logLoc.'Downloading metadata from '.var_export($this->url, true));
119
+        Logger::debug($this->logLoc . 'Downloading metadata from ' . var_export($this->url, true));
120 120
 
121 121
         $context = ['ssl' => []];
122 122
         if ($this->sslCAFile !== null) {
123 123
             $context['ssl']['cafile'] = Config::getCertPath($this->sslCAFile);
124
-            Logger::debug($this->logLoc.'Validating https connection against CA certificate(s) found in '.
124
+            Logger::debug($this->logLoc . 'Validating https connection against CA certificate(s) found in ' .
125 125
                 var_export($context['ssl']['cafile'], true));
126 126
             $context['ssl']['verify_peer'] = true;
127 127
             $context['ssl']['CN_match'] = parse_url($this->url, PHP_URL_HOST);
@@ -129,26 +129,26 @@  discard block
 block discarded – undo
129 129
 
130 130
         $data = HTTP::fetch($this->url, $context);
131 131
         if ($data === false || $data === null) {
132
-            Logger::error($this->logLoc.'Unable to load metadata from '.var_export($this->url, true));
132
+            Logger::error($this->logLoc . 'Unable to load metadata from ' . var_export($this->url, true));
133 133
             return null;
134 134
         }
135 135
 
136 136
         $doc = new \DOMDocument();
137 137
         $res = $doc->loadXML($data);
138 138
         if (!$res) {
139
-            Logger::error($this->logLoc.'Error parsing XML from '.var_export($this->url, true));
139
+            Logger::error($this->logLoc . 'Error parsing XML from ' . var_export($this->url, true));
140 140
             return null;
141 141
         }
142 142
 
143 143
         $root = Utils::xpQuery($doc->firstChild, '/saml_metadata:EntityDescriptor|/saml_metadata:EntitiesDescriptor');
144 144
         if (count($root) === 0) {
145
-            Logger::error($this->logLoc.'No <EntityDescriptor> or <EntitiesDescriptor> in metadata from '.
145
+            Logger::error($this->logLoc . 'No <EntityDescriptor> or <EntitiesDescriptor> in metadata from ' .
146 146
                 var_export($this->url, true));
147 147
             return null;
148 148
         }
149 149
 
150 150
         if (count($root) > 1) {
151
-            Logger::error($this->logLoc.'More than one <EntityDescriptor> or <EntitiesDescriptor> in metadata from '.
151
+            Logger::error($this->logLoc . 'More than one <EntityDescriptor> or <EntitiesDescriptor> in metadata from ' .
152 152
                 var_export($this->url, true));
153 153
             return null;
154 154
         }
@@ -161,8 +161,8 @@  discard block
 block discarded – undo
161 161
                 $md = new EntitiesDescriptor($root);
162 162
             }
163 163
         } catch (\Exception $e) {
164
-            Logger::error($this->logLoc.'Unable to parse metadata from '.
165
-                var_export($this->url, true).': '.$e->getMessage());
164
+            Logger::error($this->logLoc . 'Unable to parse metadata from ' .
165
+                var_export($this->url, true) . ': ' . $e->getMessage());
166 166
             return null;
167 167
         }
168 168
 
@@ -170,7 +170,7 @@  discard block
 block discarded – undo
170 170
             $file = Config::getCertPath($this->certificate);
171 171
             $certData = file_get_contents($file);
172 172
             if ($certData === false) {
173
-                throw new Exception('Error loading certificate from '.var_export($file, true));
173
+                throw new Exception('Error loading certificate from ' . var_export($file, true));
174 174
             }
175 175
 
176 176
             // Extract the public key from the certificate for validation
@@ -178,10 +178,10 @@  discard block
 block discarded – undo
178 178
             $key->loadKey($file, true);
179 179
 
180 180
             if (!$md->validate($key)) {
181
-                Logger::error($this->logLoc.'Error validating signature on metadata.');
181
+                Logger::error($this->logLoc . 'Error validating signature on metadata.');
182 182
                 return null;
183 183
             }
184
-            Logger::debug($this->logLoc.'Validated signature on metadata from '.var_export($this->url, true));
184
+            Logger::debug($this->logLoc . 'Validated signature on metadata from ' . var_export($this->url, true));
185 185
         }
186 186
 
187 187
         return $md;
@@ -244,7 +244,7 @@  discard block
 block discarded – undo
244 244
             return null;
245 245
         }
246 246
 
247
-        Logger::debug($this->logLoc.'Using cached metadata from '.var_export($cacheFile, true));
247
+        Logger::debug($this->logLoc . 'Using cached metadata from ' . var_export($cacheFile, true));
248 248
 
249 249
         $metadata = file_get_contents($cacheFile);
250 250
         if ($metadata !== null) {
Please login to merge, or discard this patch.
www/get.php 2 patches
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -1,18 +1,18 @@  discard block
 block discarded – undo
1 1
 <?php
2 2
 
3 3
 if (!isset($_REQUEST['id'])) {
4
-    throw new \SimpleSAML\Error\BadRequest('Missing required parameter "id".');
4
+	throw new \SimpleSAML\Error\BadRequest('Missing required parameter "id".');
5 5
 }
6 6
 $id = strval($_REQUEST['id']);
7 7
 
8 8
 $set = null;
9 9
 if (isset($_REQUEST['set'])) {
10
-    $set = explode(',', $_REQUEST['set']);
10
+	$set = explode(',', $_REQUEST['set']);
11 11
 }
12 12
 
13 13
 $excluded_entities = null;
14 14
 if (isset($_REQUEST['exclude'])) {
15
-    $excluded_entities = explode(',', $_REQUEST['exclude']);
15
+	$excluded_entities = explode(',', $_REQUEST['exclude']);
16 16
 }
17 17
 
18 18
 $aggregator = \SimpleSAML\Module\aggregator2\Aggregator::getAggregator($id);
@@ -22,17 +22,17 @@  discard block
 block discarded – undo
22 22
 
23 23
 $mimetype = 'application/samlmetadata+xml';
24 24
 $allowedmimetypes = [
25
-    'text/plain',
26
-    'application/samlmetadata-xml',
27
-    'application/xml',
25
+	'text/plain',
26
+	'application/samlmetadata-xml',
27
+	'application/xml',
28 28
 ];
29 29
 
30 30
 if (isset($_GET['mimetype']) && in_array($_GET['mimetype'], $allowedmimetypes)) {
31
-    $mimetype = $_GET['mimetype'];
31
+	$mimetype = $_GET['mimetype'];
32 32
 }
33 33
 
34 34
 if ($mimetype === 'text/plain') {
35
-    $xml = \SimpleSAML\Utils\XML::formatXMLString($xml);
35
+	$xml = \SimpleSAML\Utils\XML::formatXMLString($xml);
36 36
 }
37 37
 
38 38
 header('Content-Type: '.$mimetype);
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -35,13 +35,13 @@
 block discarded – undo
35 35
     $xml = \SimpleSAML\Utils\XML::formatXMLString($xml);
36 36
 }
37 37
 
38
-header('Content-Type: '.$mimetype);
38
+header('Content-Type: ' . $mimetype);
39 39
 header('Content-Length: ' . strlen($xml));
40 40
 
41 41
 /*
42 42
  * At this point, if the ID was forged, getMetadata() would
43 43
  * have failed to find a valid metadata set, so we can trust it.
44 44
  */
45
-header('Content-Disposition: filename='.$id.'.xml');
45
+header('Content-Disposition: filename=' . $id . '.xml');
46 46
 
47 47
 echo $xml;
Please login to merge, or discard this patch.