Passed
Pull Request — master (#363)
by Tim
02:17
created

ServiceProvider::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 11
dl 0
loc 18
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\Entity;
6
7
use Exception;
8
use Psr\Http\Message\ServerRequestInterface;
9
use SimpleSAML\Assert\Assert;
10
use SimpleSAML\SAML2\{
11
    Binding,
12
    Metadata,
13
    MetadataProviderInterface,
14
    StateProviderInterface,
15
    StorageProviderInterface,
16
    Utils,
17
};
18
use SimpleSAML\SAML2\Binding\HTTPArtifact;
19
use SimpleSAML\SAML2\Exception\{MetadataNotFoundException, RemoteException, RuntimeException};
0 ignored issues
show
Bug introduced by
The type SimpleSAML\SAML2\Exception\RemoteException 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...
20
use SimpleSAML\SAML2\Exception\Protocol\{RequestDeniedException, ResourceNotRecognizedException};
21
use SimpleSAML\SAML2\Process\Validator\ResponseValidator;
22
use SimpleSAML\SAML2\XML\saml\{
23
    Assertion,
24
    AttributeStatement,
25
    EncryptedAssertion,
26
    EncryptedAttribute,
27
    EncryptedID,
28
    Subject,
29
};
30
use SimpleSAML\SAML2\XML\samlp\Response;
31
use SimpleSAML\XMLSecurity\Alg\Encryption\EncryptionAlgorithmFactory;
32
use SimpleSAML\XMLSecurity\Alg\KeyTransport\KeyTransportAlgorithmFactory;
33
use SimpleSAML\XMLSecurity\Alg\Signature\SignatureAlgorithmFactory;
34
use SimpleSAML\XMLSecurity\Exception\SignatureVerificationFailedException;
35
use SimpleSAML\XMLSecurity\XML\{
36
    EncryptableElementInterface,
37
    EncryptedElementInterface,
38
    SignableElementInterface,
39
    SignedElementInterface,
40
};
41
42
use function sprintf;
43
44
/**
45
 * Class representing a SAML 2 Service Provider.
46
 *
47
 * @package simplesamlphp/saml2
48
 */
49
final class ServiceProvider
50
{
51
    protected ?StateProviderInterface $stateProvider = null;
52
    protected ?StorageProviderInterface $storageProvider = null;
53
    protected ?Metadata\IdentityProvider $idpMetadata = null;
54
    protected SignatureAlgorithmFactory $signatureAlgorithmFactory;
55
    protected EncryptionAlgorithmFactory $encryptionAlgorithmFactory;
56
    protected KeyTransportAlgorithmFactory $keyTransportAlgorithmFactory;
57
    protected bool $responseWasSigned;
58
59
    /**
60
     * @param bool $encryptedAssertions  Whether assertions must be encrypted
61
     * @param bool $disableScoping  Whether to send the samlp:Scoping element in requests
62
     * @param bool $enableUnsolicited  Whether to process unsolicited responses
63
     * @param bool $encryptNameId  Whether to encrypt the NameID sent
64
     * @param bool $signAuthnRequest  Whether to sign the AuthnRequest sent
65
     * @param bool $signLogout  Whether to sign the LogoutRequest/LogoutResponse sent
66
     * @param bool $validateLogout  Whether to validate the signature of LogoutRequest/LogoutResponse received
67
     */
68
    public function __construct(
69
        protected MetadataProviderInterface $metadataProvider,
70
        protected Metadata\ServiceProvider $spMetadata,
71
        protected readonly bool $encryptedAssertions = false,
72
        protected readonly bool $disableScoping = false,
73
        protected readonly bool $enableUnsolicited = false,
74
        protected readonly bool $encryptNameId = false,
75
        protected readonly bool $signAuthnRequest = false,
76
        protected readonly bool $signLogout = false,
77
        protected readonly bool $validateLogout = true,
78
        // Use with caution - will leave any form of signature verification or token decryption up to the implementer
79
        protected readonly bool $bypassResponseVerification = false,
80
        // Use with caution - will leave any form of constraint validation up to the implementer
81
        protected readonly bool $bypassConstraintValidation = false,
82
    ) {
83
        $this->signatureAlgorithmFactory = new SignatureAlgorithmFactory();
84
        $this->encryptionAlgorithmFactory = new EncryptionAlgorithmFactory();
85
        $this->keyTransportAlgorithmFactory = new KeyTransportAlgorithmFactory();
86
    }
87
88
89
    /**
90
     */
91
    public function setStateProvider(StateProviderInterface $stateProvider): void
92
    {
93
        $this->stateProvider = $stateProvider;
94
    }
95
96
97
    /**
98
     */
99
    public function setStorageProvider(StorageProviderInterface $storageProvider): void
100
    {
101
        $this->storageProvider = $storageProvider;
102
    }
103
104
105
    /**
106
     * Receive a verified, and optionally validated Response.
107
     *
108
     * Upon receiving the response from the binding, the signature will be validated first.
109
     * Once the signature checks out, the assertions are decrypted, their signatures verified
110
     *  and then any encrypted NameID's and/or attributes are decrypted.
111
     *
112
     * @param \Psr\Http\Message\ServerRequestInterface $request
113
     * @return \SimpleSAML\SAML2\XML\samlp\Response The validated response.
114
     *
115
     * @throws \SimpleSAML\SAML2\Exception\Protocol\UnsupportedBindingException
116
     */
117
    public function receiveResponse(ServerRequestInterface $request): Response
118
    {
119
        $binding = Binding::getCurrentBinding($request);
120
121
        if ($binding instanceof HTTPArtifact) {
122
            if ($this->storageProvider === null) {
123
                throw new RuntimeException(
124
                    "A StorageProvider is required to use the HTTP-Artifact binding.",
125
                );
126
            }
127
128
            $artifact = $binding->receiveArtifact($request);
0 ignored issues
show
Bug introduced by
The method receiveArtifact() does not exist on SimpleSAML\SAML2\Binding\HTTPArtifact. Did you maybe mean receive()? ( Ignorable by Annotation )

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

128
            /** @scrutinizer ignore-call */ 
129
            $artifact = $binding->receiveArtifact($request);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
129
            $this->idpMetadata = $this->metadataProvider->getIdPMetadataForSha1($artifact->getSourceId());
130
131
            if ($this->idpMetadata === null) {
132
                throw new MetadataNotFoundException(sprintf(
133
                    'No metadata found for remote entity with SHA1 ID: %s',
134
                    $artifact->getSourceId(),
135
                ));
136
            }
137
138
            $binding->setIdpMetadata($this->idpMetadata);
0 ignored issues
show
Bug introduced by
The method setIdpMetadata() does not exist on SimpleSAML\SAML2\Binding\HTTPArtifact. ( Ignorable by Annotation )

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

138
            $binding->/** @scrutinizer ignore-call */ 
139
                      setIdpMetadata($this->idpMetadata);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
139
            $binding->setSPMetadata($this->spMetadata);
140
        }
141
142
        $rawResponse = $binding->receive($request);
143
        Assert::isInstanceOf($rawResponse, Response::class, ResourceNotRecognizedException::class); // Wrong type of msg
144
145
        // Will return a raw Response prior to any form of verification
146
        if ($this->bypassResponseVerification === true) {
147
            return $rawResponse;
148
        }
149
150
        // Fetch the metadata for the remote entity
151
        if (!($binding instanceof HTTPArtifact)) {
152
            $this->idpMetadata = $this->metadataProvider->getIdPMetadata($rawResponse->getIssuer()->getContent());
153
154
            if ($this->idpMetadata === null) {
155
                throw new MetadataNotFoundException(sprintf(
156
                    'No metadata found for remote entity with entityID: %s',
157
                    $rawResponse->getIssuer()->getContent(),
158
                ));
159
            }
160
        }
161
162
        // Verify the signature (if any)
163
        $this->responseWasSigned = $rawResponse->isSigned();
164
        $verifiedResponse = $this->responseWasSigned ? $this->verifyElementSignature($rawResponse) : $rawResponse;
165
166
        $state = null;
167
        $stateId = $verifiedResponse->getInResponseTo();
0 ignored issues
show
Bug introduced by
The method getInResponseTo() does not exist on SimpleSAML\SAML2\XML\samlp\AbstractMessage. It seems like you code against a sub-type of SimpleSAML\SAML2\XML\samlp\AbstractMessage such as SimpleSAML\SAML2\XML\samlp\AbstractStatusResponse. ( Ignorable by Annotation )

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

167
        /** @scrutinizer ignore-call */ 
168
        $stateId = $verifiedResponse->getInResponseTo();
Loading history...
Bug introduced by
The method getInResponseTo() does not exist on SimpleSAML\SAML2\XML\samlp\LogoutRequest. ( Ignorable by Annotation )

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

167
        /** @scrutinizer ignore-call */ 
168
        $stateId = $verifiedResponse->getInResponseTo();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getInResponseTo() does not exist on SimpleSAML\XMLSecurity\X...ignableElementInterface. It seems like you code against a sub-type of SimpleSAML\XMLSecurity\X...ignableElementInterface such as SimpleSAML\SAML2\XML\samlp\AbstractStatusResponse. ( Ignorable by Annotation )

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

167
        /** @scrutinizer ignore-call */ 
168
        $stateId = $verifiedResponse->getInResponseTo();
Loading history...
Bug introduced by
The method getInResponseTo() does not exist on SimpleSAML\SAML2\XML\samlp\ArtifactResolve. ( Ignorable by Annotation )

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

167
        /** @scrutinizer ignore-call */ 
168
        $stateId = $verifiedResponse->getInResponseTo();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getInResponseTo() does not exist on SimpleSAML\SAML2\XML\samlp\AttributeQuery. ( Ignorable by Annotation )

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

167
        /** @scrutinizer ignore-call */ 
168
        $stateId = $verifiedResponse->getInResponseTo();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getInResponseTo() does not exist on SimpleSAML\SAML2\XML\samlp\AuthnRequest. ( Ignorable by Annotation )

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

167
        /** @scrutinizer ignore-call */ 
168
        $stateId = $verifiedResponse->getInResponseTo();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
168
169
        if (!empty($stateId)) {
170
            if ($this->stateProvider === null) {
171
                throw new RuntimeException(
172
                    "A StateProvider is required to correlate responses to their initial request.",
173
                );
174
            }
175
176
            // this should be a response to a request we sent earlier
177
            try {
178
                $state = $this->stateProvider::loadState($stateId, 'saml:sp:sso');
179
            } catch (RuntimeException $e) {
180
                // something went wrong,
181
                Utils::getContainer()->getLogger()->warning(sprintf(
182
                    'Could not load state specified by InResponseTo: %s; processing response as unsolicited.',
183
                    $e->getMessage(),
184
                ));
185
            }
186
        }
187
188
        $issuer = $verifiedResponse->getIssuer()->getContent();
0 ignored issues
show
Bug introduced by
The method getIssuer() does not exist on SimpleSAML\XMLSecurity\X...ignableElementInterface. It seems like you code against a sub-type of SimpleSAML\XMLSecurity\X...ignableElementInterface such as SimpleSAML\SAML2\XML\samlp\AbstractMessage or SimpleSAML\SAML2\XML\saml\Assertion. ( Ignorable by Annotation )

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

188
        $issuer = $verifiedResponse->/** @scrutinizer ignore-call */ getIssuer()->getContent();
Loading history...
189
        if ($state === null) {
190
            if ($this->enableUnsolicited === false) {
191
                throw new RequestDeniedException('Unsolicited responses are denied by configuration.');
192
            }
193
        } else {
194
            // check that the issuer is the one we are expecting
195
            Assert::keyExists($state, 'ExpectedIssuer');
196
197
            if ($state['ExpectedIssuer'] !== $issuer) {
198
                throw new ResourceNotRecognizedException("Issuer doesn't match the one the AuthnRequest was sent to.");
199
            }
200
        }
201
202
        $this->idpMetadata = $this->metadataProvider->getIdPMetadata($issuer);
203
        if ($this->idpMetadata === null) {
204
            throw new MetadataNotFoundException(sprintf(
205
                'No metadata found for remote identity provider with entityID: %s',
206
                $issuer,
207
            ));
208
        }
209
210
        $responseValidator = ResponseValidator::createResponseValidator(
211
            $this->idpMetadata,
212
            $this->spMetadata,
213
            $binding,
214
        );
215
        $responseValidator->validate($verifiedResponse);
216
217
        if ($this->encryptedAssertions === true) {
218
            Assert::allIsInstanceOf($verifiedResponse->getAssertions(), EncryptedAssertion::class);
0 ignored issues
show
Bug introduced by
The method getAssertions() does not exist on SimpleSAML\SAML2\XML\samlp\AuthnRequest. ( Ignorable by Annotation )

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

218
            Assert::allIsInstanceOf($verifiedResponse->/** @scrutinizer ignore-call */ getAssertions(), EncryptedAssertion::class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getAssertions() does not exist on SimpleSAML\SAML2\XML\samlp\AbstractMessage. It seems like you code against a sub-type of SimpleSAML\SAML2\XML\samlp\AbstractMessage such as SimpleSAML\SAML2\XML\samlp\Response. ( Ignorable by Annotation )

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

218
            Assert::allIsInstanceOf($verifiedResponse->/** @scrutinizer ignore-call */ getAssertions(), EncryptedAssertion::class);
Loading history...
Bug introduced by
The method getAssertions() does not exist on SimpleSAML\XMLSecurity\X...ignableElementInterface. It seems like you code against a sub-type of SimpleSAML\XMLSecurity\X...ignableElementInterface such as SimpleSAML\SAML2\XML\samlp\Response. ( Ignorable by Annotation )

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

218
            Assert::allIsInstanceOf($verifiedResponse->/** @scrutinizer ignore-call */ getAssertions(), EncryptedAssertion::class);
Loading history...
Bug introduced by
The method getAssertions() does not exist on SimpleSAML\SAML2\XML\samlp\LogoutRequest. ( Ignorable by Annotation )

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

218
            Assert::allIsInstanceOf($verifiedResponse->/** @scrutinizer ignore-call */ getAssertions(), EncryptedAssertion::class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getAssertions() does not exist on SimpleSAML\SAML2\XML\samlp\LogoutResponse. ( Ignorable by Annotation )

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

218
            Assert::allIsInstanceOf($verifiedResponse->/** @scrutinizer ignore-call */ getAssertions(), EncryptedAssertion::class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getAssertions() does not exist on SimpleSAML\SAML2\XML\samlp\ArtifactResolve. ( Ignorable by Annotation )

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

218
            Assert::allIsInstanceOf($verifiedResponse->/** @scrutinizer ignore-call */ getAssertions(), EncryptedAssertion::class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getAssertions() does not exist on SimpleSAML\SAML2\XML\samlp\ArtifactResponse. ( Ignorable by Annotation )

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

218
            Assert::allIsInstanceOf($verifiedResponse->/** @scrutinizer ignore-call */ getAssertions(), EncryptedAssertion::class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getAssertions() does not exist on SimpleSAML\SAML2\XML\samlp\AttributeQuery. ( Ignorable by Annotation )

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

218
            Assert::allIsInstanceOf($verifiedResponse->/** @scrutinizer ignore-call */ getAssertions(), EncryptedAssertion::class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
219
        }
220
221
        // Decrypt and verify assertions, then rebuild the response.
222
        $verifiedAssertions = $this->decryptAndVerifyAssertions($verifiedResponse->getAssertions());
223
        $decryptedResponse = new Response(
224
            $verifiedResponse->getStatus(),
0 ignored issues
show
Bug introduced by
The method getStatus() does not exist on SimpleSAML\SAML2\XML\samlp\LogoutRequest. ( Ignorable by Annotation )

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

224
            $verifiedResponse->/** @scrutinizer ignore-call */ 
225
                               getStatus(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getStatus() does not exist on SimpleSAML\SAML2\XML\samlp\AttributeQuery. ( Ignorable by Annotation )

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

224
            $verifiedResponse->/** @scrutinizer ignore-call */ 
225
                               getStatus(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getStatus() does not exist on SimpleSAML\SAML2\XML\samlp\ArtifactResolve. ( Ignorable by Annotation )

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

224
            $verifiedResponse->/** @scrutinizer ignore-call */ 
225
                               getStatus(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getStatus() does not exist on SimpleSAML\XMLSecurity\X...ignableElementInterface. It seems like you code against a sub-type of SimpleSAML\XMLSecurity\X...ignableElementInterface such as SimpleSAML\SAML2\XML\samlp\AbstractStatusResponse. ( Ignorable by Annotation )

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

224
            $verifiedResponse->/** @scrutinizer ignore-call */ 
225
                               getStatus(),
Loading history...
Bug introduced by
The method getStatus() does not exist on SimpleSAML\SAML2\XML\samlp\AbstractMessage. It seems like you code against a sub-type of SimpleSAML\SAML2\XML\samlp\AbstractMessage such as SimpleSAML\SAML2\XML\samlp\AbstractStatusResponse. ( Ignorable by Annotation )

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

224
            $verifiedResponse->/** @scrutinizer ignore-call */ 
225
                               getStatus(),
Loading history...
Bug introduced by
The method getStatus() does not exist on SimpleSAML\SAML2\XML\samlp\AuthnRequest. ( Ignorable by Annotation )

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

224
            $verifiedResponse->/** @scrutinizer ignore-call */ 
225
                               getStatus(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
225
            $verifiedResponse->getIssueInstant(),
0 ignored issues
show
Bug introduced by
The method getIssueInstant() does not exist on SimpleSAML\XMLSecurity\X...ignableElementInterface. It seems like you code against a sub-type of SimpleSAML\XMLSecurity\X...ignableElementInterface such as SimpleSAML\SAML2\XML\samlp\AbstractMessage or SimpleSAML\SAML2\XML\saml\Assertion. ( Ignorable by Annotation )

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

225
            $verifiedResponse->/** @scrutinizer ignore-call */ 
226
                               getIssueInstant(),
Loading history...
226
            $verifiedResponse->getIssuer(),
227
            $verifiedResponse->getID(),
228
            $verifiedResponse->getVersion(),
0 ignored issues
show
Bug introduced by
The method getVersion() does not exist on SimpleSAML\XMLSecurity\X...ignableElementInterface. It seems like you code against a sub-type of SimpleSAML\XMLSecurity\X...ignableElementInterface such as SimpleSAML\SAML2\XML\samlp\AbstractMessage. ( Ignorable by Annotation )

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

228
            $verifiedResponse->/** @scrutinizer ignore-call */ 
229
                               getVersion(),
Loading history...
229
            $verifiedResponse->getInResponseTo(),
230
            $verifiedResponse->getDestination(),
0 ignored issues
show
Bug introduced by
The method getDestination() does not exist on SimpleSAML\XMLSecurity\X...ignableElementInterface. It seems like you code against a sub-type of SimpleSAML\XMLSecurity\X...ignableElementInterface such as SimpleSAML\SAML2\XML\samlp\AbstractMessage. ( Ignorable by Annotation )

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

230
            $verifiedResponse->/** @scrutinizer ignore-call */ 
231
                               getDestination(),
Loading history...
231
            $verifiedResponse->getConsent(),
0 ignored issues
show
Bug introduced by
The method getConsent() does not exist on SimpleSAML\XMLSecurity\X...ignableElementInterface. It seems like you code against a sub-type of SimpleSAML\XMLSecurity\X...ignableElementInterface such as SimpleSAML\SAML2\XML\samlp\AbstractMessage. ( Ignorable by Annotation )

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

231
            $verifiedResponse->/** @scrutinizer ignore-call */ 
232
                               getConsent(),
Loading history...
232
            $verifiedResponse->getExtensions(),
0 ignored issues
show
Bug introduced by
The method getExtensions() does not exist on SimpleSAML\XMLSecurity\X...ignableElementInterface. It seems like you code against a sub-type of SimpleSAML\XMLSecurity\X...ignableElementInterface such as SimpleSAML\SAML2\XML\samlp\AbstractMessage or SimpleSAML\SAML2\XML\md\AbstractMetadataDocument. ( Ignorable by Annotation )

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

232
            $verifiedResponse->/** @scrutinizer ignore-call */ 
233
                               getExtensions(),
Loading history...
233
            $verifiedAssertions,
234
        );
235
236
237
        // Will return a verified and fully decrypted Response prior to any form of validation
238
        if ($this->bypassConstraintValidation === true) {
239
            return $decryptedResponse;
240
        }
241
242
        // TODO: Validate assertions
243
        return $decryptedResponse;
244
    }
245
246
247
    /**
248
     * Process the assertions and decrypt any encrypted elements inside.
249
     *
250
     * @param \SimpleSAML\SAML2\XML\saml\Assertion[] $unverifiedAssertions
251
     * @return \SimpleSAML\SAML2\XML\saml\Assertion[]
252
     *
253
     * @throws \SimpleSAML\SAML2\Exception\RuntimeException if none of the keys could be used to decrypt the element
254
     */
255
    protected function decryptAndVerifyAssertions(array $unverifiedAssertions): array
256
    {
257
        $wantAssertionsSigned = $this->spMetadata->getWantAssertionsSigned();
258
259
        /**
260
         * See paragraph 6.2 of the SAML 2.0 core specifications for the applicable processing rules
261
         *
262
         * Long story short - Decrypt the assertion first, then validate it's signature
263
         * Once the signature is verified, decrypt any BaseID, NameID or Attribute that's encrypted
264
         */
265
        $verifiedAssertions = [];
266
        foreach ($unverifiedAssertions as $i => $assertion) {
267
            // Decrypt the assertions
268
            $decryptedAssertion = ($assertion instanceof EncryptedAssertion)
269
                ? $this->decryptElement($assertion)
270
                : $assertion;
271
272
            // Verify that the request is signed, if we require this by configuration
273
            if ($wantAssertionsSigned === true) {
274
                Assert::true($decryptedAssertion->isSigned(), RuntimeException::class);
0 ignored issues
show
Bug introduced by
The method isSigned() does not exist on SimpleSAML\XMLSecurity\X...yptableElementInterface. It seems like you code against a sub-type of SimpleSAML\XMLSecurity\X...yptableElementInterface such as SimpleSAML\SAML2\XML\saml\Assertion. ( Ignorable by Annotation )

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

274
                Assert::true($decryptedAssertion->/** @scrutinizer ignore-call */ isSigned(), RuntimeException::class);
Loading history...
275
            }
276
277
            // Verify the signature on the assertions (if any)
278
            $verifiedAssertion = $this->verifyElementSignature($decryptedAssertion);
0 ignored issues
show
Bug introduced by
It seems like $decryptedAssertion can also be of type SimpleSAML\XMLSecurity\X...yptableElementInterface; however, parameter $element of SimpleSAML\SAML2\Entity\...erifyElementSignature() does only seem to accept SimpleSAML\XMLSecurity\XML\SignedElementInterface, 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

278
            $verifiedAssertion = $this->verifyElementSignature(/** @scrutinizer ignore-type */ $decryptedAssertion);
Loading history...
279
280
            // Decrypt the NameID and replace it inside the assertion's Subject
281
            $nameID = $verifiedAssertion->getSubject()?->getIdentifier();
282
283
            if ($nameID instanceof EncryptedID) {
284
                $decryptedNameID = $this->decryptElement($nameID);
285
                // Anything we can't decrypt, we leave up for the application to deal with
286
                try {
287
                    $subject = new Subject($decryptedNameID, $verifiedAssertion->getSubjectConfirmation());
0 ignored issues
show
Bug introduced by
The method getSubjectConfirmation() does not exist on SimpleSAML\SAML2\XML\saml\Assertion. Did you maybe mean getSubject()? ( Ignorable by Annotation )

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

287
                    $subject = new Subject($decryptedNameID, $verifiedAssertion->/** @scrutinizer ignore-call */ getSubjectConfirmation());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
288
                } catch (RuntimeException) {
289
                    $subject = $verifiedAssertion->getSubject();
290
                }
291
            } else {
292
                $subject = $verifiedAssertion->getSubject();
293
            }
294
295
            // Decrypt any occurrences of EncryptedAttribute and replace them inside the assertion's AttributeStatement
296
            $statements = $verifiedAssertion->getStatements();
297
            foreach ($verifiedAssertion->getStatements() as $j => $statement) {
298
                if ($statement instanceof AttributeStatement) {
299
                    $attributes = $statement->getAttributes();
300
                    if ($statement->hasEncryptedAttributes()) {
301
                        foreach ($statement->getEncryptedAttributes() as $encryptedAttribute) {
302
                            // Anything we can't decrypt, we leave up for the application to deal with
303
                            try {
304
                                $attributes[] = $this->decryptElement($encryptedAttribute);
305
                            } catch (RuntimeException) {
306
                                $attributes[] = $encryptedAttribute;
307
                            }
308
                        }
309
                    }
310
311
                    $statements[$j] = new AttributeStatement($attributes);
312
                }
313
            }
314
315
            // Rebuild the Assertion
316
            $verifiedAssertions[] = new Assertion(
317
                $verifiedAssertion->getIssuer(),
318
                $verifiedAssertion->getIssueInstant(),
319
                $verifiedAssertion->getID(),
320
                $subject,
321
                $verifiedAssertion->getConditions(),
322
                $statements,
323
            );
324
        }
325
326
        return $verifiedAssertions;
327
    }
328
329
330
    /**
331
     * Decrypt the given element using the decryption keys provided to us.
332
     *
333
     * @param \SimpleSAML\XMLSecurity\XML\EncryptedElementInterface $element
334
     * @return \SimpleSAML\XMLSecurity\EncryptableElementInterface
0 ignored issues
show
Bug introduced by
The type SimpleSAML\XMLSecurity\EncryptableElementInterface 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...
335
     *
336
     * @throws \SimpleSAML\SAML2\Exception\RuntimeException if none of the keys could be used to decrypt the element
337
     */
338
    protected function decryptElement(EncryptedElementInterface $element): EncryptableElementInterface
339
    {
340
        $factory = $this->encryptionAlgorithmFactory;
341
342
        // If the IDP has a pre-shared key, try decrypting with that
343
        $preSharedKey = $this->idpMetadata->getPreSharedKey();
0 ignored issues
show
Bug introduced by
The method getPreSharedKey() does not exist on null. ( Ignorable by Annotation )

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

343
        /** @scrutinizer ignore-call */ 
344
        $preSharedKey = $this->idpMetadata->getPreSharedKey();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
344
        if ($preSharedKey !== null) {
345
            $encryptionAlgorithm = $element?->getEncryptedKey()?->getEncryptionMethod()
346
              ?? $this->idpMetadata->getPreSharedKeyAlgorithm();
347
348
            $decryptor = $factory->getAlgorithm($encryptionAlgorithm, $preSharedKey);
349
            try {
350
                return $element->decrypt($decryptor);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $element->decrypt($decryptor) returns the type SimpleSAML\XML\ElementInterface which is incompatible with the type-hinted return SimpleSAML\XMLSecurity\X...yptableElementInterface.
Loading history...
351
            } catch (Exception $e) {
352
                // Continue to try decrypting with asymmetric keys.
353
            }
354
        }
355
356
        $encryptionAlgorithm = $element->getEncryptedKey()->getEncryptionMethod()->getAlgorithm();
357
        foreach ($this->spMetadata->getDecryptionKeys() as $decryptionKey) {
358
            $factory = $this->keyTransportAlgorithmFactory;
359
            $decryptor = $factory->getAlgorithm($encryptionAlgorithm, $decryptionKey);
360
            try {
361
                return $element->decrypt($decryptor);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $element->decrypt($decryptor) returns the type SimpleSAML\XML\ElementInterface which is incompatible with the type-hinted return SimpleSAML\XMLSecurity\X...yptableElementInterface.
Loading history...
362
            } catch (Exception $e) {
363
                continue;
364
            }
365
        }
366
367
        throw new RuntimeException(sprintf(
368
            'Unable to decrypt %s with any of the available keys.',
369
            $element::class,
370
        ));
371
    }
372
373
374
    /**
375
     * Verify the signature of an element using the available validation keys.
376
     *
377
     * @param \SimpleSAML\XMLSecurity\XML\SignedElementInterface $element
378
     * @return \SimpleSAML\XMLSecurity\XML\SignableElementInterface The validated element.
379
     *
380
     * @throws \SimpleSAML\XMLSecurity\Exception\SignatureVerificationFailedException
381
     */
382
    protected function verifyElementSignature(SignedElementInterface $element): SignableElementInterface
383
    {
384
        $signatureAlgorithm = $element->getSignature()->getSignedInfo()->getSignatureMethod()->getAlgorithm();
385
386
        foreach ($this->idpMetadata->getValidatingKeys() as $validatingKey) {
387
            $verifier = $this->signatureAlgorithmFactory->getAlgorithm($signatureAlgorithm, $validatingKey);
388
389
            try {
390
                return $element->verify($verifier);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $element->verify($verifier) returns the type SimpleSAML\XMLSecurity\XML\SignedElementInterface which is incompatible with the type-hinted return SimpleSAML\XMLSecurity\X...ignableElementInterface.
Loading history...
391
            } catch (SignatureVerificationFailedException $e) {
392
                continue;
393
            }
394
        }
395
396
        throw new SignatureVerificationFailedException();
397
    }
398
}
399