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

EntitySource::downloadMetadata()   C

Complexity

Conditions 12
Paths 28

Size

Total Lines 71
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 48
c 0
b 0
f 0
nc 28
nop 0
dl 0
loc 71
rs 6.9666

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\Configuration;
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...
6
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...
7
use \SimpleSAML\Logger;
8
use \SimpleSAML\Utils\Config;
9
use \SimpleSAML\Utils\HTTP;
10
11
use \SAML2\Utils;
12
use \SAML2\XML\md\EntitiesDescriptor;
13
use \SAML2\XML\md\EntityDescriptor;
14
15
/**
16
 * Class for loading metadata from files and URLs.
17
 *
18
 * @package SimpleSAMLphp
19
 */
20
class EntitySource
21
{
22
    /**
23
     * Our log "location".
24
     *
25
     * @var string
26
     */
27
    protected $logLoc;
28
29
    /**
30
     * The aggregator we belong to.
31
     *
32
     * @var sspmod_aggregator2_Aggregator
0 ignored issues
show
Bug introduced by
The type SimpleSAML\Module\aggreg..._aggregator2_Aggregator 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...
33
     */
34
    protected $aggregator;
35
36
    /**
37
     * The URL we should fetch it from.
38
     *
39
     * @var string
40
     */
41
    protected $url;
42
43
    /**
44
     * The SSL CA file that should be used to validate the connection.
45
     *
46
     * @var string|null
47
     */
48
    protected $sslCAFile;
49
50
    /**
51
     * The certificate we should use to validate downloaded metadata.
52
     *
53
     * @var string|null
54
     */
55
    protected $certificate;
56
57
    /**
58
     * The parsed metadata.
59
     *
60
     * @var \SAML2\XML\md\EntitiesDescriptor|\SAML2\XML\md\EntityDescriptor|null
61
     */
62
    protected $metadata;
63
64
    /**
65
     * The cache ID.
66
     *
67
     * @var string
68
     */
69
    protected $cacheId;
70
71
    /**
72
     * The cache tag.
73
     *
74
     * @var string
75
     */
76
    protected $cacheTag;
77
78
    /**
79
     * Whether we have attempted to update the cache already.
80
     *
81
     * @var bool
82
     */
83
    protected $updateAttempted;
84
85
86
    /**
87
     * Initialize this EntitySource.
88
     *
89
     * @param \SimpleSAML\Configuration $config  The configuration.
90
     */
91
    public function __construct(Aggregator $aggregator, Configuration $config)
92
    {
93
        $this->logLoc = 'aggregator2:'.$aggregator->getId().': ';
94
        $this->aggregator = $aggregator;
0 ignored issues
show
Documentation Bug introduced by
It seems like $aggregator of type SimpleSAML\Module\aggregator2\Aggregator is incompatible with the declared type SimpleSAML\Module\aggreg..._aggregator2_Aggregator of property $aggregator.

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...
95
96
        $this->url = $config->getString('url');
97
        $this->sslCAFile = $config->getString('ssl.cafile', null);
98
        if ($this->sslCAFile === null) {
99
            $this->sslCAFile = $aggregator->getCAFile();
100
        }
101
102
        $this->certificate = $config->getString('cert', null);
103
104
        $this->cacheId = sha1($this->url);
105
        $this->cacheTag = sha1(serialize($config));
106
    }
107
108
109
    /**
110
     * Retrieve and parse the metadata.
111
     *
112
     * @return \SAML2\XML\md\EntitiesDescriptor|\SAML2\XML\md\EntityDescriptor|null
113
     * The downloaded metadata or NULL if we were unable to download or parse it.
114
     */
115
    private function downloadMetadata()
116
    {
117
        Logger::debug($this->logLoc.'Downloading metadata from '.var_export($this->url, true));
118
119
        $context = ['ssl' => []];
120
        if ($this->sslCAFile !== null) {
121
            $context['ssl']['cafile'] = Config::getCertPath($this->sslCAFile);
122
            SimpleSAML\Logger::debug($this->logLoc.'Validating https connection against CA certificate(s) found in '.
0 ignored issues
show
Bug introduced by
The type SimpleSAML\Module\aggregator2\SimpleSAML\Logger was not found. Did you mean SimpleSAML\Logger? If so, make sure to prefix the type with \.
Loading history...
123
                var_export($context['ssl']['cafile'], true));
124
            $context['ssl']['verify_peer'] = true;
125
            $context['ssl']['CN_match'] = parse_url($this->url, PHP_URL_HOST);
126
        }
127
128
        $data = HTTP::fetch($this->url, $context);
129
        if ($data === false || $data === null) {
130
            Logger::error($this->logLoc.'Unable to load metadata from '.var_export($this->url, true));
131
            return null;
132
        }
133
134
        $doc = new DOMDocument();
0 ignored issues
show
Bug introduced by
The type SimpleSAML\Module\aggregator2\DOMDocument was not found. Did you mean DOMDocument? If so, make sure to prefix the type with \.
Loading history...
135
        $res = $doc->loadXML($data);
136
        if (!$res) {
137
            Logger::error($this->logLoc.'Error parsing XML from '.var_export($this->url, true));
138
            return null;
139
        }
140
141
        $root = Utils::xpQuery($doc->firstChild, '/saml_metadata:EntityDescriptor|/saml_metadata:EntitiesDescriptor');
142
        if (count($root) === 0) {
143
            Logger::error($this->logLoc.'No <EntityDescriptor> or <EntitiesDescriptor> in metadata from '.
144
                var_export($this->url, true));
145
            return null;
146
        }
147
148
        if (count($root) > 1) {
149
            Logger::error($this->logLoc.'More than one <EntityDescriptor> or <EntitiesDescriptor> in metadata from '.
150
                var_export($this->url, true));
151
            return null;
152
        }
153
154
        $root = $root[0];
155
        try {
156
            if ($root->localName === 'EntityDescriptor') {
157
                $md = new EntityDescriptor($root);
158
            } else {
159
                $md = new EntitiesDescriptor($root);
160
            }
161
        } catch (\Exception $e) {
162
            Logger::error($this->logLoc.'Unable to parse metadata from '.
163
                var_export($this->url, true).': '.$e->getMessage());
164
            return null;
165
        }
166
167
        if ($this->certificate !== null) {
168
            $file = Config::getCertPath($this->certificate);
169
            $certData = file_get_contents($file);
170
            if ($certData === false) {
171
                throw new Exception('Error loading certificate from '.var_export($file, true));
172
            }
173
174
            // Extract the public key from the certificate for validation
175
            $key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, ['type'=>'public']);
0 ignored issues
show
Bug introduced by
The type SimpleSAML\Module\aggregator2\XMLSecurityKey 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...
176
            $key->loadKey($file, true);
177
178
            if (!$md->validate($key)) {
179
                Logger::error($this->logLoc.'Error validating signature on metadata.');
180
                return null;
181
            }
182
            Logger::debug($this->logLoc.'Validated signature on metadata from '.var_export($this->url, true));
183
        }
184
185
        return $md;
186
    }
187
188
189
    /**
190
     * Attempt to update our cache file.
191
     */
192
    public function updateCache()
193
    {
194
        if ($this->updateAttempted) {
195
            return;
196
        }
197
        $this->updateAttempted = true;
198
199
        $this->metadata = $this->downloadMetadata();
200
        if ($this->metadata === null) {
201
            return;
202
        }
203
204
        $expires = time() + 24*60*60; // Default expires in one day
205
206
        if ($this->metadata->validUntil !== null && $this->metadata->validUntil < $expires) {
207
            $expires = $this->metadata->validUntil;
208
        }
209
210
        $metadataSerialized = serialize($this->metadata);
211
212
        $this->aggregator->addCacheItem($this->cacheId, $metadataSerialized, $expires, $this->cacheTag);
213
    }
214
215
216
    /**
217
     * Retrieve the metadata file.
218
     *
219
     * This function will check its cached copy, to see whether it can be used.
220
     *
221
     * @return \SAML2\XML\md\EntityDescriptor|\SAML2\XML\md\EntitiesDescriptor|null  The downloaded metadata.
222
     */
223
    public function getMetadata()
224
    {
225
        if ($this->metadata !== null) {
226
            /* We have already downloaded the metdata. */
227
            return $this->metadata;
228
        }
229
230
        if (!$this->aggregator->isCacheValid($this->cacheId, $this->cacheTag)) {
231
            $this->updateCache();
232
            if ($this->metadata !== null) {
233
                return $this->metadata;
234
            }
235
            /* We were unable to update the cache - use cached metadata. */
236
        }
237
238
        $cacheFile = $this->aggregator->getCacheFile($this->cacheId);
239
240
        if (!file_exists($cacheFile)) {
241
            Logger::error($this->logLoc . 'No cached metadata available.');
242
            return null;
243
        }
244
245
        Logger::debug($this->logLoc.'Using cached metadata from '.var_export($cacheFile, true));
246
247
        $metadata = file_get_contents($cacheFile);
248
        if ($metadata !== null) {
249
            $this->metadata = unserialize($metadata);
250
            return $this->metadata;
251
        }
252
253
        return null;
254
    }
255
}
256