Passed
Push — master ( b399c9...7b0af8 )
by Tim
02:06
created

Aggregator::exclude()   A

Complexity

Conditions 6
Paths 7

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 12
c 0
b 0
f 0
nc 7
nop 1
dl 0
loc 22
rs 9.2222
1
<?php
2
3
namespace SimpleSAML\Module\aggregator2;
4
5
use \SimpleSAML\Error\Exception;
0 ignored issues
show
Bug introduced by
The type SimpleSAML\Error\Exception was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use \SimpleSAML\Utils\Configuration;
0 ignored issues
show
Bug introduced by
The type SimpleSAML\Utils\Configuration was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7
use \SimpleSAML\Utils\Logger;
0 ignored issues
show
Bug introduced by
The type SimpleSAML\Utils\Logger was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use \SimpleSAML\Utils\System;
9
10
use \SAML2\SignedElement;
11
use \SAML2_Utils;
0 ignored issues
show
Bug introduced by
The type SAML2_Utils was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use \SAML2\XML\md\EntitiesDescriptor;
13
use \SAML2\XML\md\EntityDescriptor;
14
use \SAML2\XML\mdrpi\RegistrationInfo;
15
16
use \RobRichards\XMLSecLibs\XMLSecurityKey;
17
18
/**
19
 * Class which implements a basic metadata aggregator.
20
 *
21
 * @package SimpleSAMLphp
22
 */
23
class Aggregator
24
{
25
    /**
26
     * The list of signature algorithms supported by the aggregator.
27
     *
28
     * @var array
29
     */
30
    public static $SUPPORTED_SIGNATURE_ALGORITHMS = [
31
        XMLSecurityKey::RSA_SHA1,
32
        XMLSecurityKey::RSA_SHA256,
33
        XMLSecurityKey::RSA_SHA384,
34
        XMLSecurityKey::RSA_SHA512,
35
    ];
36
37
    /**
38
     * The ID of this aggregator.
39
     *
40
     * @var string
41
     */
42
    protected $id;
43
44
    /**
45
     * Our log "location".
46
     *
47
     * @var string
48
     */
49
    protected $logLoc;
50
51
    /**
52
     * Which cron-tag this should be updated in.
53
     *
54
     * @var string|null
55
     */
56
    protected $cronTag;
57
58
    /**
59
     * Absolute path to a cache directory.
60
     *
61
     * @var string|null
62
     */
63
    protected $cacheDirectory;
64
65
    /**
66
     * The entity sources.
67
     *
68
     * Array of sspmod_aggregator2_EntitySource objects.
69
     *
70
     * @var array
71
     */
72
    protected $sources = [];
73
74
    /**
75
     * How long the generated metadata should be valid, as a number of seconds.
76
     *
77
     * This is used to set the validUntil attribute on the generated EntityDescriptor.
78
     *
79
     * @var int
80
     */
81
    protected $validLength;
82
83
    /**
84
     * Duration we should cache generated metadata.
85
     *
86
     * @var int
87
     */
88
    protected $cacheGenerated;
89
90
    /**
91
     * An array of entity IDs to exclude from the aggregate.
92
     *
93
     * @var string[]|null
94
     */
95
    protected $excluded;
96
97
    /**
98
     * An indexed array of protocols to filter the aggregate by. keys can be any of:
99
     *
100
     * - urn:oasis:names:tc:SAML:1.1:protocol
101
     * - urn:oasis:names:tc:SAML:2.0:protocol
102
     *
103
     * Values will be true if enabled, false otherwise.
104
     *
105
     * @var string[]|null
106
     */
107
    protected $protocols;
108
109
    /**
110
     * An array of roles to filter the aggregate by. Keys can be any of:
111
     *
112
     * - SAML2_XML_md_IDPSSODescriptor
113
     * - SAML2_XML_md_SPSSODescriptor
114
     * - SAML2_XML_md_AttributeAuthorityDescriptor
115
     *
116
     * Values will be true if enabled, false otherwise.
117
     *
118
     * @var string[]|null
119
     */
120
    protected $roles;
121
122
    /**
123
     * The key we should use to sign the metadata.
124
     *
125
     * @var string|null
126
     */
127
    protected $signKey;
128
129
    /**
130
     * The password for the private key.
131
     *
132
     * @var string|null
133
     */
134
    protected $signKeyPass;
135
136
    /**
137
     * The certificate of the key we sign the metadata with.
138
     *
139
     * @var string|null
140
     */
141
    protected $signCert;
142
143
    /**
144
     * The algorithm to use for metadata signing.
145
     *
146
     * @var string|null
147
     */
148
    protected $signAlg;
149
150
    /**
151
     * The CA certificate file that should be used to validate https-connections.
152
     *
153
     * @var string|null
154
     */
155
    protected $sslCAFile;
156
157
    /**
158
     * The cache ID for our generated metadata.
159
     *
160
     * @var string
161
     */
162
    protected $cacheId;
163
164
    /**
165
     * The cache tag for our generated metadata.
166
     *
167
     * This tag is used to make sure that a config change
168
     * invalidates our cached metadata.
169
     *
170
     * @var string
171
     */
172
    protected $cacheTag;
173
174
    /**
175
     * The registration information for our generated metadata.
176
     *
177
     * @var array
178
     */
179
    protected $regInfo;
180
181
182
    /**
183
     * Initialize this aggregator.
184
     *
185
     * @param string $id  The id of this aggregator.
186
     * @param \SimpleSAML\Configuration $config  The configuration for this aggregator.
0 ignored issues
show
Bug introduced by
The type SimpleSAML\Configuration was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
187
     */
188
    protected function __construct($id, Configuration $config)
189
    {
190
        assert('is_string($id)');
191
192
        $this->id = $id;
193
        $this->logLoc = 'aggregator2:'.$this->id.': ';
194
195
        $this->cronTag = $config->getString('cron.tag', null);
196
197
        $this->cacheDirectory = $config->getString('cache.directory', null);
198
        if ($this->cacheDirectory !== null) {
199
            $this->cacheDirectory = System::resolvePath($this->cacheDirectory);
200
        }
201
202
        $this->cacheGenerated = $config->getInteger('cache.generated', null);
203
        if ($this->cacheGenerated !== null) {
204
            $this->cacheId = sha1($this->id);
205
            $this->cacheTag = sha1(serialize($config));
206
        }
207
208
        // configure entity IDs excluded by default
209
        $this->excludeEntities($config->getArrayize('exclude', null));
210
211
        // configure filters
212
        $this->setFilters($config->getArrayize('filter', null));
213
214
        $this->validLength = $config->getInteger('valid.length', 7*24*60*60);
215
216
        $globalConfig = Configuration::getInstance();
217
        $certDir = $globalConfig->getPathValue('certdir', 'cert/');
218
219
        $signKey = $config->getString('sign.privatekey', null);
220
        if ($signKey !== null) {
221
            $signKey = System::resolvePath($signKey, $certDir);
222
            $this->signKey = @file_get_contents($signKey);
223
            if ($this->signKey === null) {
224
                throw new Exception('Unable to load private key from '.var_export($signKey, true));
225
            }
226
        }
227
228
        $this->signKeyPass = $config->getString('sign.privatekey_pass', null);
229
230
        $signCert = $config->getString('sign.certificate', null);
231
        if ($signCert !== null) {
232
            $signCert = System::resolvePath($signCert, $certDir);
233
            $this->signCert = @file_get_contents($signCert);
234
            if ($this->signCert === null) {
235
                throw new Exception('Unable to load certificate file from '.var_export($signCert, true));
236
            }
237
        }
238
239
        $this->signAlg = $config->getString('sign.algorithm', XMLSecurityKey::RSA_SHA1);
240
        if (!in_array($this->signAlg, self::$SUPPORTED_SIGNATURE_ALGORITHMS)) {
241
            throw new Exception('Unsupported signature algorithm '.var_export($this->signAlg, true));
242
        }
243
244
        $this->sslCAFile = $config->getString('ssl.cafile', null);
245
246
        $this->regInfo = $config->getArray('RegistrationInfo', null);
247
248
        $this->initSources($config->getConfigList('sources'));
249
    }
250
251
252
    /**
253
     * Populate the sources array.
254
     *
255
     * This is called from the constructor, and can be overridden in subclasses.
256
     *
257
     * @param array $sources  The sources as an array of SimpleSAML_Configuration objects.
258
     */
259
    protected function initSources(array $sources)
260
    {
261
        foreach ($sources as $source) {
262
            $this->sources[] = new EntitySource($this, $source);
263
        }
264
    }
265
266
267
    /**
268
     * Return an instance of the aggregator with the given id.
269
     *
270
     * @param string $id  The id of the aggregator.
271
     */
272
    public static function getAggregator($id)
273
    {
274
        assert('is_string($id)');
275
276
        $config = Configuration::getConfig('module_aggregator2.php');
277
        return new Aggregator($id, $config->getConfigItem($id));
278
    }
279
280
281
    /**
282
     * Retrieve the ID of the aggregator.
283
     *
284
     * @return string  The ID of this aggregator.
285
     */
286
    public function getId()
287
    {
288
        return $this->id;
289
    }
290
291
292
    /**
293
     * Add an item to the cache.
294
     *
295
     * @param string $id  The identifier of this data.
296
     * @param string $data  The data.
297
     * @param int $expires  The timestamp the data expires.
298
     * @param string|null $tag  An extra tag that can be used to verify the validity of the cached data.
299
     */
300
    public function addCacheItem($id, $data, $expires, $tag = null)
301
    {
302
        assert('is_string($id)');
303
        assert('is_string($data)');
304
        assert('is_int($expires)');
305
        assert('is_null($tag) || is_string($tag)');
306
307
        $cacheFile = $this->cacheDirectory.'/'.$id;
308
        try {
309
            System::writeFile($cacheFile, $data);
310
        } catch (\Exception $e) {
311
            Logger::warning($this->logLoc.'Unable to write to cache file '.var_export($cacheFile, true));
312
            return;
313
        }
314
315
        $expireInfo = (string)$expires;
316
        if ($tag !== null) {
317
            $expireInfo .= ':'.$tag;
318
        }
319
320
        $expireFile = $cacheFile.'.expire';
321
        try {
322
            System::writeFile($expireFile, $expireInfo);
323
        } catch (\Exception $e) {
324
            Logger::warning($this->logLoc.'Unable to write expiration info to '.var_export($expireFile, true));
325
        }
326
    }
327
328
329
    /**
330
     * Check validity of cached data.
331
     *
332
     * @param string $id  The identifier of this data.
333
     * @param string $tag  The tag that was passed to addCacheItem.
334
     * @return bool  TRUE if the data is valid, FALSE if not.
335
     */
336
    public function isCacheValid($id, $tag = null)
337
    {
338
        assert('is_string($id)');
339
        assert('is_null($tag) || is_string($tag)');
340
341
        $cacheFile = $this->cacheDirectory.'/'.$id;
342
        if (!file_exists($cacheFile)) {
343
            return false;
344
        }
345
346
        $expireFile = $cacheFile.'.expire';
347
        if (!file_exists($expireFile)) {
348
            return false;
349
        }
350
351
        $expireData = @file_get_contents($expireFile);
352
        if ($expireData === false) {
353
            return false;
354
        }
355
356
        $expireData = explode(':', $expireData, 2);
357
358
        $expireTime = intval($expireData[0]);
359
        if ($expireTime <= time()) {
360
            return false;
361
        }
362
363
        if (count($expireData) === 1) {
364
            $expireTag = null;
365
        } else {
366
            $expireTag = $expireData[1];
367
        }
368
        if ($expireTag !== $tag) {
369
            return false;
370
        }
371
372
        return true;
373
    }
374
375
376
    /**
377
     * Get the cache item.
378
     *
379
     * @param string $id  The identifier of this data.
380
     * @param string $tag  The tag that was passed to addCacheItem.
381
     * @return string|null  The cache item, or NULL if it isn't cached or if it is expired.
382
     */
383
    public function getCacheItem($id, $tag = null)
384
    {
385
        assert('is_string($id)');
386
        assert('is_null($tag) || is_string($tag)');
387
388
        if (!$this->isCacheValid($id, $tag)) {
389
            return null;
390
        }
391
392
        $cacheFile = $this->cacheDirectory.'/'.$id;
393
        return @file_get_contents($cacheFile);
394
    }
395
396
397
    /**
398
     * Get the cache filename for the specific id.
399
     *
400
     * @param string $id  The identifier of the cached data.
401
     * @return string|null  The filename, or NULL if the cache file doesn't exist.
402
     */
403
    public function getCacheFile($id)
404
    {
405
        assert('is_string($id)');
406
407
        $cacheFile = $this->cacheDirectory.'/'.$id;
408
        if (!file_exists($cacheFile)) {
409
            return null;
410
        }
411
412
        return $cacheFile;
413
    }
414
415
416
    /**
417
     * Retrieve the SSL CA file path, if it is set.
418
     *
419
     * @return string|null  The SSL CA file path.
420
     */
421
    public function getCAFile()
422
    {
423
        return $this->sslCAFile;
424
    }
425
426
427
    /**
428
     * Sign the generated EntitiesDescriptor.
429
     */
430
    protected function addSignature(SignedElement $element)
431
    {
432
        if ($this->signKey === null) {
433
            return;
434
        }
435
436
        $privateKey = new XMLSecurityKey($this->signAlg, ['type' => 'private']);
437
        if ($this->signKeyPass !== null) {
438
            $privateKey->passphrase = $this->signKeyPass;
439
        }
440
        $privateKey->loadKey($this->signKey, false);
441
442
        $element->setSignatureKey($privateKey);
443
444
        if ($this->signCert !== null) {
445
            $element->setCertificates([$this->signCert]);
446
        }
447
    }
448
449
450
    /**
451
     * Recursively browse the children of an EntitiesDescriptor element looking for EntityDescriptor elements, and
452
     * return an array containing all of them.
453
     *
454
     * @param \SAML2\XML\md\EntitiesDescriptor $entity The source EntitiesDescriptor that holds the entities to extract.
455
     *
456
     * @return array An array containing all the EntityDescriptors found.
457
     */
458
    private static function extractEntityDescriptors($entity)
459
    {
460
        assert('$entity instanceof EntitiesDescriptor');
461
462
        if (!($entity instanceof EntitiesDescriptor)) {
0 ignored issues
show
introduced by
$entity is always a sub-type of SAML2\XML\md\EntitiesDescriptor.
Loading history...
463
            return [];
464
        }
465
466
        $results = [];
467
        foreach ($entity->children as $child) {
468
            if ($child instanceof EntityDescriptor) {
469
                $results[] = $child;
470
                continue;
471
            }
472
473
            $results = array_merge($results, self::extractEntityDescriptors($child));
474
        }
475
        return $results;
476
    }
477
478
479
    /**
480
     * Retrieve all entities as an EntitiesDescriptor.
481
     *
482
     * @return \SAML2\XML\md\EntitiesDescriptor  The entities.
483
     */
484
    protected function getEntitiesDescriptor()
485
    {
486
        $ret = new EntitiesDescriptor();
487
        $now = time();
488
489
        // add RegistrationInfo extension if enabled
490
        if ($this->regInfo !== null) {
491
            $ri = new RegistrationInfo();
492
            $ri->registrationInstant = $now;
493
            foreach ($this->regInfo as $riName => $riValues) {
494
                switch ($riName) {
495
                    case 'authority':
496
                        $ri->registrationAuthority = $riValues;
497
                        break;
498
                    case 'instant':
499
                        $ri->registrationInstant = Utils::xsDateTimeToTimestamp($riValues);
0 ignored issues
show
Bug introduced by
The type SimpleSAML\Module\aggregator2\Utils was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
500
                        break;
501
                    case 'policies':
502
                        $ri->RegistrationPolicy = $riValues;
503
                        break;
504
                }
505
            }
506
            $ret->Extensions[] = $ri;
507
        }
508
509
        foreach ($this->sources as $source) {
510
            $m = $source->getMetadata();
511
            if ($m === NULL) {
512
                continue;
513
            }
514
            if ($m instanceof EntityDescriptor) {
515
                $ret->children[] = $m;
516
            } elseif ($m instanceof EntitiesDescriptor) {
517
                $ret->children = array_merge($ret->children, self::extractEntityDescriptors($m));
518
            }
519
        }
520
521
        $ret->children = array_unique($ret->children, SORT_REGULAR);
522
        $ret->validUntil = $now + $this->validLength;
523
524
        return $ret;
525
    }
526
527
528
    /**
529
     * Recursively traverse the children of an EntitiesDescriptor, removing those entities listed in the $entities
530
     * property. Returns the EntitiesDescriptor with the entities filtered out.
531
     *
532
     * @param \SAML2\XML\md\EntitiesDescriptor $descriptor The EntitiesDescriptor from where to exclude entities.
533
     *
534
     * @return \SAML2\XML\md\EntitiesDescriptor The EntitiesDescriptor with excluded entities filtered out.
535
     */
536
    protected function exclude(EntitiesDescriptor $descriptor)
537
    {
538
        if (empty($this->excluded)) {
539
            return $descriptor;
540
        }
541
542
        $filtered = [];
543
        foreach ($descriptor->children as $child) {
544
            if ($child instanceof EntityDescriptor) {
545
                if (in_array($child->entityID, $this->excluded)) {
546
                    continue;
547
                }
548
                $filtered[] = $child;
549
            }
550
551
            if ($child instanceof EntitiesDescriptor) {
552
                $filtered[] = $this->exclude($child);
553
            }
554
        }
555
556
        $descriptor->children = $filtered;
557
        return $descriptor;
558
    }
559
560
561
    /**
562
     * Recursively traverse the children of an EntitiesDescriptor, keeping only those entities with the roles listed in
563
     * the $roles property, and support for the protocols listed in the $protocols property. Returns the
564
     * EntitiesDescriptor containing only those entities.
565
     *
566
     * @param \SAML2\XML\md\EntitiesDescriptor $descriptor The EntitiesDescriptor to filter.
567
     *
568
     * @return SAML2_XML_md_EntitiesDescriptor The EntitiesDescriptor with only the entities filtered.
569
     */
570
    protected function filter(SAML2_XML_md_EntitiesDescriptor $descriptor)
0 ignored issues
show
Bug introduced by
The type SimpleSAML\Module\aggreg...L_md_EntitiesDescriptor was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
571
    {
572
        if ($this->roles === null || $this->protocols === null) {
573
            return $descriptor;
574
        }
575
576
        $enabled_roles = array_keys($this->roles, true);
577
        $enabled_protos = array_keys($this->protocols, true);
578
579
        $filtered = [];
580
        foreach ($descriptor->children as $child) {
581
            if ($child instanceof EntityDescriptor) {
582
                foreach ($child->RoleDescriptor as $role) {
583
                    if (in_array(get_class($role), $enabled_roles)) {
584
                        // we found a role descriptor that is enabled by our filters, check protocols
585
                        if (array_intersect($enabled_protos, $role->protocolSupportEnumeration) !== []) {
586
                            // it supports some protocol we have enabled, add it
587
                            $filtered[] = $child;
588
                            break;
589
                        }
590
                    }
591
                }
592
593
            }
594
595
            if ($child instanceof EntitiesDescriptor) {
596
                $filtered[] = $this->filter($child);
597
            }
598
        }
599
600
        $descriptor->children = $filtered;
601
        return $descriptor;
602
    }
603
604
605
    /**
606
     * Set this aggregator to exclude a set of entities from the resulting aggregate.
607
     *
608
     * @param array|null $entities The entity IDs of the entities to exclude.
609
     */
610
    public function excludeEntities($entities)
611
    {
612
        assert('is_array($entities) || is_null($entities)');
613
614
        if ($entities === null) {
615
            return;
616
        }
617
        $this->excluded = $entities;
618
        sort($this->excluded);
619
        $this->cacheId = sha1($this->cacheId.serialize($this->excluded));
620
    }
621
622
623
    /**
624
     * Set the internal filters according to one or more options:
625
     *
626
     * - 'saml2': all SAML2.0-capable entities.
627
     * - 'shib13': all SHIB1.3-capable entities.
628
     * - 'saml20-idp': all SAML2.0-capable identity providers.
629
     * - 'saml20-sp': all SAML2.0-capable service providers.
630
     * - 'saml20-aa': all SAML2.0-capable attribute authorities.
631
     * - 'shib13-idp': all SHIB1.3-capable identity providers.
632
     * - 'shib13-sp': all SHIB1.3-capable service providers.
633
     * - 'shib13-aa': all SHIB1.3-capable attribute authorities.
634
     *
635
     * @param array|null $set An array of the different roles and protocols to filter by.
636
     */
637
    public function setFilters($set)
638
    {
639
        assert('is_array($set) || is_null($set)');
640
641
        if ($set === null) {
642
            return;
643
        }
644
645
        // configure filters
646
        $this->protocols = [
0 ignored issues
show
Documentation Bug introduced by
It seems like array(SimpleSAML\Module\...:1.1:protocol' => true) of type array<mixed|string,true> is incompatible with the declared type null|string[] of property $protocols.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
647
            SAML2_Const::NS_SAMLP                  => true,
0 ignored issues
show
Bug introduced by
The type SimpleSAML\Module\aggregator2\SAML2_Const was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
648
            'urn:oasis:names:tc:SAML:1.1:protocol' => true,
649
        ];
650
        $this->roles = [
0 ignored issues
show
Documentation Bug introduced by
It seems like array('SAML2_XML_md_IDPS...ityDescriptor' => true) of type array<string,true> is incompatible with the declared type null|string[] of property $roles.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
651
            'SAML2_XML_md_IDPSSODescriptor'             => true,
652
            'SAML2_XML_md_SPSSODescriptor'              => true,
653
            'SAML2_XML_md_AttributeAuthorityDescriptor' => true,
654
        ];
655
656
        // now translate from the options we have, to specific protocols and roles
657
658
        // check SAML 2.0 protocol
659
        $options = ['saml2', 'saml20-idp', 'saml20-sp', 'saml20-aa'];
660
        $this->protocols[SAML2_Const::NS_SAMLP] = (array_intersect($set, $options) !== []);
661
662
        // check SHIB 1.3 protocol
663
        $options = ['shib13', 'shib13-idp', 'shib13-sp', 'shib13-aa'];
664
        $this->protocols['urn:oasis:names:tc:SAML:1.1:protocol'] = (array_intersect($set, $options) !== []);
665
666
        // check IdP
667
        $options = ['saml2', 'shib13', 'saml20-idp', 'shib13-idp'];
668
        $this->roles['SAML2_XML_md_IDPSSODescriptor'] = (array_intersect($set, $options) !== []);
669
670
        // check SP
671
        $options = ['saml2', 'shib13', 'saml20-sp', 'shib13-sp'];
672
        $this->roles['SAML2_XML_md_SPSSODescriptor'] = (array_intersect($set, $options) !== []);
673
674
        // check AA
675
        $options = ['saml2', 'shib13', 'saml20-aa', 'shib13-aa'];
676
        $this->roles['SAML2_XML_md_AttributeAuthorityDescriptor'] = (array_intersect($set, $options) !== []);
677
678
        $this->cacheId = sha1($this->cacheId.serialize($this->protocols).serialize($this->roles));
679
    }
680
681
682
    /**
683
     * Retrieve the complete, signed metadata as text.
684
     *
685
     * This function will write the new metadata to the cache file, but will not return
686
     * the cached metadata.
687
     *
688
     * @return string  The metadata, as text.
689
     */
690
    public function updateCachedMetadata()
691
    {
692
        $ed = $this->getEntitiesDescriptor();
693
        $ed = $this->exclude($ed);
694
        $ed = $this->filter($ed);
695
        $this->addSignature($ed);
696
697
        $xml = $ed->toXML();
698
        $xml = $xml->ownerDocument->saveXML($xml);
699
700
        if ($this->cacheGenerated !== null) {
701
            Logger::debug($this->logLoc.'Saving generated metadata to cache.');
702
            $this->addCacheItem($this->cacheId, $xml, time() + $this->cacheGenerated, $this->cacheTag);
703
        }
704
705
        return $xml;
706
    }
707
708
709
    /**
710
     * Retrieve the complete, signed metadata as text.
711
     *
712
     * @return string  The metadata, as text.
713
     */
714
    public function getMetadata()
715
    {
716
        if ($this->cacheGenerated !== null) {
717
            $xml = $this->getCacheItem($this->cacheId, $this->cacheTag);
718
            if ($xml !== null) {
719
                Logger::debug($this->logLoc.'Loaded generated metadata from cache.');
720
                return $xml;
721
            }
722
        }
723
724
        return $this->updateCachedMetadata();
725
    }
726
727
728
    /**
729
     * Update the cached copy of our metadata.
730
     */
731
    public function updateCache()
732
    {
733
        foreach ($this->sources as $source) {
734
            $source->updateCache();
735
        }
736
737
        $this->updateCachedMetadata();
738
    }
739
}
740