Completed
Push — master ( b945ca...d48d53 )
by Jaime Pérez
13s queued 11s
created

Aggregator::getEntitiesDescriptor()   C

Complexity

Conditions 16
Paths 20

Size

Total Lines 70
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 16
eloc 52
c 2
b 0
f 0
nc 20
nop 0
dl 0
loc 70
rs 5.5666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace SimpleSAML\Module\aggregator2;
4
5
use \SimpleSAML\Error\Exception;
6
use \SimpleSAML\Configuration;
7
use \SimpleSAML\Logger;
8
use \SimpleSAML\Utils\System;
9
10
use \SAML2\Constants;
11
use \SAML2\SignedElement;
12
use \SAML2\Utils;
13
use \SAML2\XML\md\EntitiesDescriptor;
14
use \SAML2\XML\md\EntityDescriptor;
15
use \SAML2\XML\mdrpi\RegistrationInfo;
16
use \SAML2\XML\mdrpi\PublicationInfo;
17
18
use \RobRichards\XMLSecLibs\XMLSecurityKey;
19
20
/**
21
 * Class which implements a basic metadata aggregator.
22
 *
23
 * @package SimpleSAMLphp
24
 */
25
class Aggregator
26
{
27
    /**
28
     * The list of signature algorithms supported by the aggregator.
29
     *
30
     * @var array
31
     */
32
    public static $SUPPORTED_SIGNATURE_ALGORITHMS = [
33
        XMLSecurityKey::RSA_SHA1,
34
        XMLSecurityKey::RSA_SHA256,
35
        XMLSecurityKey::RSA_SHA384,
36
        XMLSecurityKey::RSA_SHA512,
37
    ];
38
39
    /**
40
     * The ID of this aggregator.
41
     *
42
     * @var string
43
     */
44
    protected $id;
45
46
    /**
47
     * Our log "location".
48
     *
49
     * @var string
50
     */
51
    protected $logLoc;
52
53
    /**
54
     * Which cron-tag this should be updated in.
55
     *
56
     * @var string|null
57
     */
58
    protected $cronTag;
59
60
    /**
61
     * Absolute path to a cache directory.
62
     *
63
     * @var string|null
64
     */
65
    protected $cacheDirectory;
66
67
    /**
68
     * The entity sources.
69
     *
70
     * Array of sspmod_aggregator2_EntitySource objects.
71
     *
72
     * @var array
73
     */
74
    protected $sources = [];
75
76
    /**
77
     * How long the generated metadata should be valid, as a number of seconds.
78
     *
79
     * This is used to set the validUntil attribute on the generated EntityDescriptor.
80
     *
81
     * @var int
82
     */
83
    protected $validLength;
84
85
    /**
86
     * Duration we should cache generated metadata.
87
     *
88
     * @var int|null
89
     */
90
    protected $cacheGenerated;
91
92
    /**
93
     * An array of entity IDs to exclude from the aggregate.
94
     *
95
     * @var string[]|null
96
     */
97
    protected $excluded;
98
99
    /**
100
     * An indexed array of protocols to filter the aggregate by. keys can be any of:
101
     *
102
     * - urn:oasis:names:tc:SAML:1.1:protocol
103
     * - urn:oasis:names:tc:SAML:2.0:protocol
104
     *
105
     * Values will be true if enabled, false otherwise.
106
     *
107
     * @var array|null
108
     */
109
    protected $protocols;
110
111
    /**
112
     * An array of roles to filter the aggregate by. Keys can be any of:
113
     *
114
     * - \SAML2\XML\md\IDPSSODescriptor
115
     * - \SAML2\XML\md\SPSSODescriptor
116
     * - \SAML2\XML\md\AttributeAuthorityDescriptor
117
     *
118
     * Values will be true if enabled, false otherwise.
119
     *
120
     * @var array|null
121
     */
122
    protected $roles;
123
124
    /**
125
     * The key we should use to sign the metadata.
126
     *
127
     * @var string|null
128
     */
129
    protected $signKey;
130
131
    /**
132
     * The password for the private key.
133
     *
134
     * @var string|null
135
     */
136
    protected $signKeyPass;
137
138
    /**
139
     * The certificate of the key we sign the metadata with.
140
     *
141
     * @var string|null
142
     */
143
    protected $signCert;
144
145
    /**
146
     * The algorithm to use for metadata signing.
147
     *
148
     * @var string|null
149
     */
150
    protected $signAlg;
151
152
    /**
153
     * The CA certificate file that should be used to validate https-connections.
154
     *
155
     * @var string|null
156
     */
157
    protected $sslCAFile;
158
159
    /**
160
     * The cache ID for our generated metadata.
161
     *
162
     * @var string
163
     */
164
    protected $cacheId = 'dummy';
165
166
    /**
167
     * The cache tag for our generated metadata.
168
     *
169
     * This tag is used to make sure that a config change
170
     * invalidates our cached metadata.
171
     *
172
     * @var string
173
     */
174
    protected $cacheTag = 'dummy';
175
176
    /**
177
     * The registration information for our generated metadata.
178
     *
179
     * @var array
180
     */
181
    protected $regInfo;
182
183
    /**
184
     * The publication information for our generated metadata.
185
     *
186
     * @var array
187
     */
188
    protected $pubInfo;
189
190
191
    /**
192
     * Initialize this aggregator.
193
     *
194
     * @param string $id  The id of this aggregator.
195
     * @param \SimpleSAML\Configuration $config  The configuration for this aggregator.
196
     */
197
    protected function __construct($id, Configuration $config)
198
    {
199
        assert('is_string($id)');
200
201
        $this->id = $id;
202
        $this->logLoc = 'aggregator2:'.$this->id.': ';
203
204
        $this->cronTag = $config->getString('cron.tag', null);
205
206
        $this->cacheDirectory = $config->getString('cache.directory', null);
207
        if ($this->cacheDirectory !== null) {
208
            $this->cacheDirectory = System::resolvePath($this->cacheDirectory);
209
        }
210
211
        $this->cacheGenerated = $config->getInteger('cache.generated', null);
212
        if ($this->cacheGenerated !== null) {
213
            $this->cacheId = sha1($this->id);
214
            $this->cacheTag = sha1(serialize($config));
215
        }
216
217
        // configure entity IDs excluded by default
218
        $this->excludeEntities($config->getArrayize('exclude', null));
219
220
        // configure filters
221
        $this->setFilters($config->getArrayize('filter', null));
222
223
        $this->validLength = $config->getInteger('valid.length', 7*24*60*60);
224
225
        $globalConfig = Configuration::getInstance();
226
        $certDir = $globalConfig->getPathValue('certdir', 'cert/');
227
228
        $signKey = $config->getString('sign.privatekey', null);
229
        if ($signKey !== null) {
230
            $signKey = System::resolvePath($signKey, $certDir);
231
            $sk = @file_get_contents($signKey);
232
            if ($sk === false) {
233
                throw new Exception('Unable to load private key from '.var_export($signKey, true));
234
            }
235
            $this->signKey = $sk;
236
        }
237
238
        $this->signKeyPass = $config->getString('sign.privatekey_pass', null);
239
240
        $signCert = $config->getString('sign.certificate', null);
241
        if ($signCert !== null) {
242
            $signCert = System::resolvePath($signCert, $certDir);
243
            $sc = @file_get_contents($signCert);
244
            if ($sc === false) {
245
                throw new Exception('Unable to load certificate file from '.var_export($signCert, true));
246
            }
247
            $this->signCert = $sc;
248
        }
249
250
        $this->signAlg = $config->getString('sign.algorithm', XMLSecurityKey::RSA_SHA1);
251
        if (!in_array($this->signAlg, self::$SUPPORTED_SIGNATURE_ALGORITHMS)) {
252
            throw new Exception('Unsupported signature algorithm '.var_export($this->signAlg, true));
253
        }
254
255
        $this->sslCAFile = $config->getString('ssl.cafile', null);
256
257
        $this->regInfo = $config->getArray('RegistrationInfo', []);
258
        $this->pubInfo = $config->getArray('PublicationInfo', []);
259
260
        $this->initSources($config->getConfigList('sources'));
0 ignored issues
show
Bug introduced by
It seems like $config->getConfigList('sources') can also be of type string; however, parameter $sources of SimpleSAML\Module\aggreg...gregator::initSources() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

260
        $this->initSources(/** @scrutinizer ignore-type */ $config->getConfigList('sources'));
Loading history...
261
    }
262
263
264
    /**
265
     * Populate the sources array.
266
     *
267
     * This is called from the constructor, and can be overridden in subclasses.
268
     *
269
     * @param array $sources  The sources as an array of \SimpleSAML\Configuration objects.
270
     * @return void
271
     */
272
    protected function initSources(array $sources)
273
    {
274
        foreach ($sources as $source) {
275
            $this->sources[] = new EntitySource($this, $source);
276
        }
277
    }
278
279
280
    /**
281
     * Return an instance of the aggregator with the given id.
282
     *
283
     * @param string $id  The id of the aggregator.
284
     * @return Aggregator
285
     */
286
    public static function getAggregator($id)
287
    {
288
        assert('is_string($id)');
289
290
        $config = Configuration::getConfig('module_aggregator2.php');
291
        return new Aggregator($id, $config->getConfigItem($id));
0 ignored issues
show
Bug introduced by
It seems like $config->getConfigItem($id) can also be of type string; however, parameter $config of SimpleSAML\Module\aggreg...gregator::__construct() does only seem to accept SimpleSAML\Configuration, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

291
        return new Aggregator($id, /** @scrutinizer ignore-type */ $config->getConfigItem($id));
Loading history...
292
    }
293
294
295
    /**
296
     * Retrieve the ID of the aggregator.
297
     *
298
     * @return string  The ID of this aggregator.
299
     */
300
    public function getId()
301
    {
302
        return $this->id;
303
    }
304
305
306
    /**
307
     * Add an item to the cache.
308
     *
309
     * @param string $id  The identifier of this data.
310
     * @param string $data  The data.
311
     * @param int $expires  The timestamp the data expires.
312
     * @param string|null $tag  An extra tag that can be used to verify the validity of the cached data.
313
     * @return void
314
     */
315
    public function addCacheItem($id, $data, $expires, $tag = null)
316
    {
317
        assert('is_string($id)');
318
        assert('is_string($data)');
319
        assert('is_int($expires)');
320
        assert('is_null($tag) || is_string($tag)');
321
322
        $cacheFile = strval($this->cacheDirectory).'/'.$id;
323
        try {
324
            System::writeFile($cacheFile, $data);
325
        } catch (\Exception $e) {
326
            Logger::warning($this->logLoc.'Unable to write to cache file '.var_export($cacheFile, true));
327
            return;
328
        }
329
330
        $expireInfo = (string)$expires;
331
        if ($tag !== null) {
332
            $expireInfo .= ':'.$tag;
333
        }
334
335
        $expireFile = $cacheFile.'.expire';
336
        try {
337
            System::writeFile($expireFile, $expireInfo);
338
        } catch (\Exception $e) {
339
            Logger::warning($this->logLoc.'Unable to write expiration info to '.var_export($expireFile, true));
340
        }
341
    }
342
343
344
    /**
345
     * Check validity of cached data.
346
     *
347
     * @param string $id  The identifier of this data.
348
     * @param string $tag  The tag that was passed to addCacheItem.
349
     * @return bool  TRUE if the data is valid, FALSE if not.
350
     */
351
    public function isCacheValid($id, $tag = null)
352
    {
353
        assert('is_string($id)');
354
        assert('is_null($tag) || is_string($tag)');
355
356
        $cacheFile = strval($this->cacheDirectory).'/'.$id;
357
        if (!file_exists($cacheFile)) {
358
            return false;
359
        }
360
361
        $expireFile = $cacheFile.'.expire';
362
        if (!file_exists($expireFile)) {
363
            return false;
364
        }
365
366
        $expireData = @file_get_contents($expireFile);
367
        if ($expireData === false) {
368
            return false;
369
        }
370
371
        $expireData = explode(':', $expireData, 2);
372
373
        $expireTime = intval($expireData[0]);
374
        if ($expireTime <= time()) {
375
            return false;
376
        }
377
378
        if (count($expireData) === 1) {
379
            $expireTag = null;
380
        } else {
381
            $expireTag = $expireData[1];
382
        }
383
        if ($expireTag !== $tag) {
384
            return false;
385
        }
386
387
        return true;
388
    }
389
390
391
    /**
392
     * Get the cache item.
393
     *
394
     * @param string $id  The identifier of this data.
395
     * @param string $tag  The tag that was passed to addCacheItem.
396
     * @return string|null  The cache item, or NULL if it isn't cached or if it is expired.
397
     */
398
    public function getCacheItem($id, $tag = null)
399
    {
400
        assert('is_string($id)');
401
        assert('is_null($tag) || is_string($tag)');
402
403
        if (!$this->isCacheValid($id, $tag)) {
404
            return null;
405
        }
406
407
        $cacheFile = strval($this->cacheDirectory).'/'.$id;
408
        return @file_get_contents($cacheFile);
409
    }
410
411
412
    /**
413
     * Get the cache filename for the specific id.
414
     *
415
     * @param string $id  The identifier of the cached data.
416
     * @return string|null  The filename, or NULL if the cache file doesn't exist.
417
     */
418
    public function getCacheFile($id)
419
    {
420
        assert('is_string($id)');
421
422
        $cacheFile = strval($this->cacheDirectory).'/'.$id;
423
        if (!file_exists($cacheFile)) {
424
            return null;
425
        }
426
427
        return $cacheFile;
428
    }
429
430
431
    /**
432
     * Retrieve the SSL CA file path, if it is set.
433
     *
434
     * @return string|null  The SSL CA file path.
435
     */
436
    public function getCAFile()
437
    {
438
        return $this->sslCAFile;
439
    }
440
441
442
    /**
443
     * Sign the generated EntitiesDescriptor.
444
     * @return void
445
     */
446
    protected function addSignature(SignedElement $element)
447
    {
448
        if ($this->signKey === null) {
449
            return;
450
        }
451
452
        /** @var string $this->signAlg */
453
        $privateKey = new XMLSecurityKey($this->signAlg, ['type' => 'private']);
454
        if ($this->signKeyPass !== null) {
455
            $privateKey->passphrase = $this->signKeyPass;
456
        }
457
        $privateKey->loadKey($this->signKey, false);
458
459
        $element->setSignatureKey($privateKey);
460
461
        if ($this->signCert !== null) {
462
            $element->setCertificates([$this->signCert]);
463
        }
464
    }
465
466
467
    /**
468
     * Recursively browse the children of an EntitiesDescriptor element looking for EntityDescriptor elements, and
469
     * return an array containing all of them.
470
     *
471
     * @param \SAML2\XML\md\EntitiesDescriptor $entity The source EntitiesDescriptor that holds the entities to extract.
472
     *
473
     * @return array An array containing all the EntityDescriptors found.
474
     */
475
    private static function extractEntityDescriptors(EntitiesDescriptor $entity)
476
    {
477
        $results = [];
478
        foreach ($entity->children as $child) {
479
            if ($child instanceof EntityDescriptor) {
480
                $results[] = $child;
481
                continue;
482
            }
483
484
            $results = array_merge($results, self::extractEntityDescriptors($child));
485
        }
486
        return $results;
487
    }
488
489
490
    /**
491
     * Retrieve all entities as an EntitiesDescriptor.
492
     *
493
     * @return \SAML2\XML\md\EntitiesDescriptor  The entities.
494
     */
495
    protected function getEntitiesDescriptor()
496
    {
497
        $ret = new EntitiesDescriptor();
498
        $now = time();
499
500
        // add RegistrationInfo extension if enabled
501
        if (!empty($this->regInfo)) {
502
            $ri = new RegistrationInfo();
503
            $ri->setRegistrationInstant($now);
504
            foreach ($this->regInfo as $riName => $riValues) {
505
                switch ($riName) {
506
                    case 'authority':
507
                        $ri->setRegistrationAuthority($riValues);
508
                        break;
509
                    case 'instant':
510
                        $ri->setRegistrationInstant(Utils::xsDateTimeToTimestamp($riValues));
511
                        break;
512
                    case 'policies':
513
                        $ri->setRegistrationPolicy($riValues);
514
                        break;
515
                    default:
516
                        Logger::warning("Unable to apply unknown configuration setting \$config['RegistrationInfo']['".strval($riValues)."'; skipping.");
517
                        break;
518
                }
519
            }
520
            $ret->Extensions[] = $ri;
521
        }
522
523
        // add PublicationInfo extension if enabled
524
        if (!empty($this->pubInfo)) {
525
            $pi = new PublicationInfo();
526
            $pi->setCreationInstant($now);
527
            foreach ($this->pubInfo as $piName => $piValues) {
528
                switch ($piName) {
529
                    case 'publisher':
530
                        $pi->setPublisher($piValues);
531
                        break;
532
                    case 'publicationId':
533
                        $pi->setPublicationId($piValues);
534
                        break;
535
                    case 'instant':
536
                        $pi->setCreationInstant(Utils::xsDateTimeToTimestamp($piValues));
537
                        break;
538
                    case 'policies':
539
                        $pi->setUsagePolicy($piValues);
540
                        break;
541
                    default:
542
                        Logger::warning("Unable to apply unknown configuration setting \$config['PublicationInfo']['".strval($piValues)."'; skipping.");
543
                        break;
544
                }
545
            }
546
            $ret->Extensions[] = $pi;
547
        }
548
549
        foreach ($this->sources as $source) {
550
            $m = $source->getMetadata();
551
            if ($m === null) {
552
                continue;
553
            }
554
            if ($m instanceof EntityDescriptor) {
555
                $ret->children[] = $m;
556
            } elseif ($m instanceof EntitiesDescriptor) {
557
                $ret->children = array_merge($ret->children, self::extractEntityDescriptors($m));
558
            }
559
        }
560
561
        $ret->children = array_unique($ret->children, SORT_REGULAR);
562
        $ret->validUntil = $now + $this->validLength;
563
564
        return $ret;
565
    }
566
567
568
    /**
569
     * Recursively traverse the children of an EntitiesDescriptor, removing those entities listed in the $entities
570
     * property. Returns the EntitiesDescriptor with the entities filtered out.
571
     *
572
     * @param \SAML2\XML\md\EntitiesDescriptor $descriptor The EntitiesDescriptor from where to exclude entities.
573
     *
574
     * @return \SAML2\XML\md\EntitiesDescriptor The EntitiesDescriptor with excluded entities filtered out.
575
     */
576
    protected function exclude(EntitiesDescriptor $descriptor)
577
    {
578
        if (empty($this->excluded)) {
579
            return $descriptor;
580
        }
581
582
        $filtered = [];
583
        foreach ($descriptor->children as $child) {
584
            if ($child instanceof EntityDescriptor) {
585
                if (in_array($child->entityID, $this->excluded)) {
586
                    continue;
587
                }
588
                $filtered[] = $child;
589
            }
590
591
            if ($child instanceof EntitiesDescriptor) {
592
                $filtered[] = $this->exclude($child);
593
            }
594
        }
595
596
        $descriptor->children = $filtered;
597
        return $descriptor;
598
    }
599
600
601
    /**
602
     * Recursively traverse the children of an EntitiesDescriptor, keeping only those entities with the roles listed in
603
     * the $roles property, and support for the protocols listed in the $protocols property. Returns the
604
     * EntitiesDescriptor containing only those entities.
605
     *
606
     * @param \SAML2\XML\md\EntitiesDescriptor $descriptor The EntitiesDescriptor to filter.
607
     *
608
     * @return \SAML2\XML\md\EntitiesDescriptor The EntitiesDescriptor with only the entities filtered.
609
     */
610
    protected function filter(EntitiesDescriptor $descriptor)
611
    {
612
        if ($this->roles === null || $this->protocols === null) {
613
            return $descriptor;
614
        }
615
616
        $enabled_roles = array_keys($this->roles, true);
617
        $enabled_protos = array_keys($this->protocols, true);
618
619
        $filtered = [];
620
        foreach ($descriptor->children as $child) {
621
            if ($child instanceof EntityDescriptor) {
622
                foreach ($child->RoleDescriptor as $role) {
623
                    if (in_array(get_class($role), $enabled_roles)) {
624
                        // we found a role descriptor that is enabled by our filters, check protocols
625
                        if (array_intersect($enabled_protos, $role->protocolSupportEnumeration) !== []) {
626
                            // it supports some protocol we have enabled, add it
627
                            $filtered[] = $child;
628
                            break;
629
                        }
630
                    }
631
                }
632
633
            }
634
635
            if ($child instanceof EntitiesDescriptor) {
636
                $filtered[] = $this->filter($child);
637
            }
638
        }
639
640
        $descriptor->children = $filtered;
641
        return $descriptor;
642
    }
643
644
645
    /**
646
     * Set this aggregator to exclude a set of entities from the resulting aggregate.
647
     *
648
     * @param array|null $entities The entity IDs of the entities to exclude.
649
     * @return void
650
     */
651
    public function excludeEntities($entities)
652
    {
653
        assert('is_array($entities) || is_null($entities)');
654
655
        if ($entities === null) {
656
            return;
657
        }
658
        $this->excluded = $entities;
659
        sort($this->excluded);
660
        $this->cacheId = sha1($this->cacheId.serialize($this->excluded));
661
    }
662
663
664
    /**
665
     * Set the internal filters according to one or more options:
666
     *
667
     * - 'saml2': all SAML2.0-capable entities.
668
     * - 'shib13': all SHIB1.3-capable entities.
669
     * - 'saml20-idp': all SAML2.0-capable identity providers.
670
     * - 'saml20-sp': all SAML2.0-capable service providers.
671
     * - 'saml20-aa': all SAML2.0-capable attribute authorities.
672
     * - 'shib13-idp': all SHIB1.3-capable identity providers.
673
     * - 'shib13-sp': all SHIB1.3-capable service providers.
674
     * - 'shib13-aa': all SHIB1.3-capable attribute authorities.
675
     *
676
     * @param array|null $set An array of the different roles and protocols to filter by.
677
     * @return void
678
     */
679
    public function setFilters($set)
680
    {
681
        assert('is_array($set) || is_null($set)');
682
683
        if ($set === null) {
684
            return;
685
        }
686
687
        // configure filters
688
        $this->protocols = [
689
            Constants::NS_SAMLP                    => true,
690
            'urn:oasis:names:tc:SAML:1.1:protocol' => true,
691
        ];
692
        $this->roles = [
693
            'SAML2_XML_md_IDPSSODescriptor'             => true,
694
            'SAML2_XML_md_SPSSODescriptor'              => true,
695
            'SAML2_XML_md_AttributeAuthorityDescriptor' => true,
696
        ];
697
698
        // now translate from the options we have, to specific protocols and roles
699
700
        // check SAML 2.0 protocol
701
        $options = ['saml2', 'saml20-idp', 'saml20-sp', 'saml20-aa'];
702
        $this->protocols[Constants::NS_SAMLP] = (array_intersect($set, $options) !== []);
703
704
        // check SHIB 1.3 protocol
705
        $options = ['shib13', 'shib13-idp', 'shib13-sp', 'shib13-aa'];
706
        $this->protocols['urn:oasis:names:tc:SAML:1.1:protocol'] = (array_intersect($set, $options) !== []);
707
708
        // check IdP
709
        $options = ['saml2', 'shib13', 'saml20-idp', 'shib13-idp'];
710
        $this->roles['SAML2_XML_md_IDPSSODescriptor'] = (array_intersect($set, $options) !== []);
711
712
        // check SP
713
        $options = ['saml2', 'shib13', 'saml20-sp', 'shib13-sp'];
714
        $this->roles['SAML2_XML_md_SPSSODescriptor'] = (array_intersect($set, $options) !== []);
715
716
        // check AA
717
        $options = ['saml2', 'shib13', 'saml20-aa', 'shib13-aa'];
718
        $this->roles['SAML2_XML_md_AttributeAuthorityDescriptor'] = (array_intersect($set, $options) !== []);
719
720
        $this->cacheId = sha1($this->cacheId.serialize($this->protocols).serialize($this->roles));
721
    }
722
723
724
    /**
725
     * Retrieve the complete, signed metadata as text.
726
     *
727
     * This function will write the new metadata to the cache file, but will not return
728
     * the cached metadata.
729
     *
730
     * @return string  The metadata, as text.
731
     */
732
    public function updateCachedMetadata()
733
    {
734
        $ed = $this->getEntitiesDescriptor();
735
        $ed = $this->exclude($ed);
736
        $ed = $this->filter($ed);
737
        $this->addSignature($ed);
738
739
        $xml = $ed->toXML();
740
        $xml = $xml->ownerDocument->saveXML($xml);
741
742
        if ($this->cacheGenerated !== null) {
743
            Logger::debug($this->logLoc.'Saving generated metadata to cache.');
744
            $this->addCacheItem($this->cacheId, $xml, time() + $this->cacheGenerated, $this->cacheTag);
745
        }
746
747
        return $xml;
748
    }
749
750
751
    /**
752
     * Retrieve the complete, signed metadata as text.
753
     *
754
     * @return string  The metadata, as text.
755
     */
756
    public function getMetadata()
757
    {
758
        if ($this->cacheGenerated !== null) {
759
            $xml = $this->getCacheItem($this->cacheId, $this->cacheTag);
760
            if ($xml !== null) {
761
                Logger::debug($this->logLoc.'Loaded generated metadata from cache.');
762
                return $xml;
763
            }
764
        }
765
766
        return $this->updateCachedMetadata();
767
    }
768
769
770
    /**
771
     * Update the cached copy of our metadata.
772
     * @return void
773
     */
774
    public function updateCache()
775
    {
776
        foreach ($this->sources as $source) {
777
            $source->updateCache();
778
        }
779
780
        $this->updateCachedMetadata();
781
    }
782
}
783