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

ServiceProvider::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
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 12
dl 0
loc 19
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 $performSchemaValidation  Whether messages must be validated against the schema
61
     * @param bool $encryptedAssertions  Whether assertions must be encrypted
62
     * @param bool $disableScoping  Whether to send the samlp:Scoping element in requests
63
     * @param bool $enableUnsolicited  Whether to process unsolicited responses
64
     * @param bool $encryptNameId  Whether to encrypt the NameID sent
65
     * @param bool $signAuthnRequest  Whether to sign the AuthnRequest sent
66
     * @param bool $signLogout  Whether to sign the LogoutRequest/LogoutResponse sent
67
     * @param bool $validateLogout  Whether to validate the signature of LogoutRequest/LogoutResponse received
68
     */
69
    public function __construct(
70
        protected MetadataProviderInterface $metadataProvider,
71
        protected Metadata\ServiceProvider $spMetadata,
72
        protected readonly bool $performSchemaValidation = true,
73
        protected readonly bool $encryptedAssertions = false,
74
        protected readonly bool $disableScoping = false,
75
        protected readonly bool $enableUnsolicited = false,
76
        protected readonly bool $encryptNameId = false,
77
        protected readonly bool $signAuthnRequest = false,
78
        protected readonly bool $signLogout = false,
79
        protected readonly bool $validateLogout = true,
80
        // Use with caution - will leave any form of signature verification or token decryption up to the implementer
81
        protected readonly bool $bypassResponseVerification = false,
82
        // Use with caution - will leave any form of constraint validation up to the implementer
83
        protected readonly bool $bypassConstraintValidation = false,
84
    ) {
85
        $this->signatureAlgorithmFactory = new SignatureAlgorithmFactory();
86
        $this->encryptionAlgorithmFactory = new EncryptionAlgorithmFactory();
87
        $this->keyTransportAlgorithmFactory = new KeyTransportAlgorithmFactory();
88
    }
89
90
91
    /**
92
     */
93
    public function setStateProvider(StateProviderInterface $stateProvider): void
94
    {
95
        $this->stateProvider = $stateProvider;
96
    }
97
98
99
    /**
100
     */
101
    public function setStorageProvider(StorageProviderInterface $storageProvider): void
102
    {
103
        $this->storageProvider = $storageProvider;
104
    }
105
106
107
    /**
108
     * Receive a verified, and optionally validated Response.
109
     *
110
     * Upon receiving the response from the binding, the signature will be validated first.
111
     * Once the signature checks out, the assertions are decrypted, their signatures verified
112
     *  and then any encrypted NameID's and/or attributes are decrypted.
113
     *
114
     * @param \Psr\Http\Message\ServerRequestInterface $request
115
     * @return \SimpleSAML\SAML2\XML\samlp\Response The validated response.
116
     *
117
     * @throws \SimpleSAML\SAML2\Exception\Protocol\UnsupportedBindingException
118
     */
119
    public function receiveResponse(ServerRequestInterface $request): Response
120
    {
121
        $binding = Binding::getCurrentBinding($request);
122
123
        if ($binding instanceof HTTPArtifact) {
124
            if ($this->storageProvider === null) {
125
                throw new RuntimeException(
126
                    "A StorageProvider is required to use the HTTP-Artifact binding.",
127
                );
128
            }
129
130
            $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

130
            /** @scrutinizer ignore-call */ 
131
            $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...
131
            $this->idpMetadata = $this->metadataProvider->getIdPMetadataForSha1($artifact->getSourceId());
132
133
            if ($this->idpMetadata === null) {
134
                throw new MetadataNotFoundException(sprintf(
135
                    'No metadata found for remote entity with SHA1 ID: %s',
136
                    $artifact->getSourceId(),
137
                ));
138
            }
139
140
            $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

140
            $binding->/** @scrutinizer ignore-call */ 
141
                      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...
141
            $binding->setSPMetadata($this->spMetadata);
142
        }
143
144
        $binding->setSchemaValidation($this->performSchemaValidation);
145
        $rawResponse = $binding->receive($request);
146
        Assert::isInstanceOf($rawResponse, Response::class, ResourceNotRecognizedException::class); // Wrong type of msg
147
148
        // Will return a raw Response prior to any form of verification
149
        if ($this->bypassResponseVerification === true) {
150
            return $rawResponse;
151
        }
152
153
        // Fetch the metadata for the remote entity
154
        if (!($binding instanceof HTTPArtifact)) {
155
            $this->idpMetadata = $this->metadataProvider->getIdPMetadata($rawResponse->getIssuer()->getContent());
156
157
            if ($this->idpMetadata === null) {
158
                throw new MetadataNotFoundException(sprintf(
159
                    'No metadata found for remote entity with entityID: %s',
160
                    $rawResponse->getIssuer()->getContent(),
161
                ));
162
            }
163
        }
164
165
        // Verify the signature (if any)
166
        $this->responseWasSigned = $rawResponse->isSigned();
167
        $verifiedResponse = $this->responseWasSigned ? $this->verifyElementSignature($rawResponse) : $rawResponse;
168
169
        $state = null;
170
        $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

170
        /** @scrutinizer ignore-call */ 
171
        $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

170
        /** @scrutinizer ignore-call */ 
171
        $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

170
        /** @scrutinizer ignore-call */ 
171
        $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

170
        /** @scrutinizer ignore-call */ 
171
        $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

170
        /** @scrutinizer ignore-call */ 
171
        $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

170
        /** @scrutinizer ignore-call */ 
171
        $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...
171
172
        if (!empty($stateId)) {
173
            if ($this->stateProvider === null) {
174
                throw new RuntimeException(
175
                    "A StateProvider is required to correlate responses to their initial request.",
176
                );
177
            }
178
179
            // this should be a response to a request we sent earlier
180
            try {
181
                $state = $this->stateProvider::loadState($stateId, 'saml:sp:sso');
182
            } catch (RuntimeException $e) {
183
                // something went wrong,
184
                Utils::getContainer()->getLogger()->warning(sprintf(
185
                    'Could not load state specified by InResponseTo: %s; processing response as unsolicited.',
186
                    $e->getMessage(),
187
                ));
188
            }
189
        }
190
191
        $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

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

221
            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

221
            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\AttributeQuery. ( Ignorable by Annotation )

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

221
            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\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

221
            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\LogoutResponse. ( Ignorable by Annotation )

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

221
            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\AuthnRequest. ( Ignorable by Annotation )

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

221
            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\LogoutRequest. ( Ignorable by Annotation )

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

221
            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

221
            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...
222
        }
223
224
        // Decrypt and verify assertions, then rebuild the response.
225
        $verifiedAssertions = $this->decryptAndVerifyAssertions($verifiedResponse->getAssertions());
226
        $decryptedResponse = new Response(
227
            $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

227
            $verifiedResponse->/** @scrutinizer ignore-call */ 
228
                               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

227
            $verifiedResponse->/** @scrutinizer ignore-call */ 
228
                               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

227
            $verifiedResponse->/** @scrutinizer ignore-call */ 
228
                               getStatus(),
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

227
            $verifiedResponse->/** @scrutinizer ignore-call */ 
228
                               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\AuthnRequest. ( Ignorable by Annotation )

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

227
            $verifiedResponse->/** @scrutinizer ignore-call */ 
228
                               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

227
            $verifiedResponse->/** @scrutinizer ignore-call */ 
228
                               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...
228
            $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

228
            $verifiedResponse->/** @scrutinizer ignore-call */ 
229
                               getIssueInstant(),
Loading history...
229
            $verifiedResponse->getIssuer(),
230
            $verifiedResponse->getID(),
231
            $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

231
            $verifiedResponse->/** @scrutinizer ignore-call */ 
232
                               getVersion(),
Loading history...
232
            $verifiedResponse->getInResponseTo(),
233
            $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

233
            $verifiedResponse->/** @scrutinizer ignore-call */ 
234
                               getDestination(),
Loading history...
234
            $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

234
            $verifiedResponse->/** @scrutinizer ignore-call */ 
235
                               getConsent(),
Loading history...
235
            $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

235
            $verifiedResponse->/** @scrutinizer ignore-call */ 
236
                               getExtensions(),
Loading history...
236
            $verifiedAssertions,
237
        );
238
239
240
        // Will return a verified and fully decrypted Response prior to any form of validation
241
        if ($this->bypassConstraintValidation === true) {
242
            return $decryptedResponse;
243
        }
244
245
        // TODO: Validate assertions
246
        return $decryptedResponse;
247
    }
248
249
250
    /**
251
     * Process the assertions and decrypt any encrypted elements inside.
252
     *
253
     * @param \SimpleSAML\SAML2\XML\saml\Assertion[] $unverifiedAssertions
254
     * @return \SimpleSAML\SAML2\XML\saml\Assertion[]
255
     *
256
     * @throws \SimpleSAML\SAML2\Exception\RuntimeException if none of the keys could be used to decrypt the element
257
     */
258
    protected function decryptAndVerifyAssertions(array $unverifiedAssertions): array
259
    {
260
        $wantAssertionsSigned = $this->spMetadata->getWantAssertionsSigned();
261
262
        /**
263
         * See paragraph 6.2 of the SAML 2.0 core specifications for the applicable processing rules
264
         *
265
         * Long story short - Decrypt the assertion first, then validate it's signature
266
         * Once the signature is verified, decrypt any BaseID, NameID or Attribute that's encrypted
267
         */
268
        $verifiedAssertions = [];
269
        foreach ($unverifiedAssertions as $i => $assertion) {
270
            // Decrypt the assertions
271
            $decryptedAssertion = ($assertion instanceof EncryptedAssertion)
272
                ? $this->decryptElement($assertion)
273
                : $assertion;
274
275
            // Verify that the request is signed, if we require this by configuration
276
            if ($wantAssertionsSigned === true) {
277
                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

277
                Assert::true($decryptedAssertion->/** @scrutinizer ignore-call */ isSigned(), RuntimeException::class);
Loading history...
278
            }
279
280
            // Verify the signature on the assertions (if any)
281
            $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

281
            $verifiedAssertion = $this->verifyElementSignature(/** @scrutinizer ignore-type */ $decryptedAssertion);
Loading history...
282
283
            // Decrypt the NameID and replace it inside the assertion's Subject
284
            $nameID = $verifiedAssertion->getSubject()?->getIdentifier();
285
286
            if ($nameID instanceof EncryptedID) {
287
                $decryptedNameID = $this->decryptElement($nameID);
288
                // Anything we can't decrypt, we leave up for the application to deal with
289
                try {
290
                    $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

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

346
        /** @scrutinizer ignore-call */ 
347
        $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...
347
        if ($preSharedKey !== null) {
348
            $encryptionAlgorithm = $element?->getEncryptedKey()?->getEncryptionMethod()
349
              ?? $this->idpMetadata->getPreSharedKeyAlgorithm();
350
351
            $decryptor = $factory->getAlgorithm($encryptionAlgorithm, $preSharedKey);
352
            try {
353
                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...
354
            } catch (Exception $e) {
355
                // Continue to try decrypting with asymmetric keys.
356
            }
357
        }
358
359
        $encryptionAlgorithm = $element->getEncryptedKey()->getEncryptionMethod()->getAlgorithm();
360
        foreach ($this->spMetadata->getDecryptionKeys() as $decryptionKey) {
361
            $factory = $this->keyTransportAlgorithmFactory;
362
            $decryptor = $factory->getAlgorithm($encryptionAlgorithm, $decryptionKey);
363
            try {
364
                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...
365
            } catch (Exception $e) {
366
                continue;
367
            }
368
        }
369
370
        throw new RuntimeException(sprintf(
371
            'Unable to decrypt %s with any of the available keys.',
372
            $element::class,
373
        ));
374
    }
375
376
377
    /**
378
     * Verify the signature of an element using the available validation keys.
379
     *
380
     * @param \SimpleSAML\XMLSecurity\XML\SignedElementInterface $element
381
     * @return \SimpleSAML\XMLSecurity\XML\SignableElementInterface The validated element.
382
     *
383
     * @throws \SimpleSAML\XMLSecurity\Exception\SignatureVerificationFailedException
384
     */
385
    protected function verifyElementSignature(SignedElementInterface $element): SignableElementInterface
386
    {
387
        $signatureAlgorithm = $element->getSignature()->getSignedInfo()->getSignatureMethod()->getAlgorithm();
388
389
        foreach ($this->idpMetadata->getValidatingKeys() as $validatingKey) {
390
            $verifier = $this->signatureAlgorithmFactory->getAlgorithm($signatureAlgorithm, $validatingKey);
391
392
            try {
393
                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...
394
            } catch (SignatureVerificationFailedException $e) {
395
                continue;
396
            }
397
        }
398
399
        throw new SignatureVerificationFailedException();
400
    }
401
}
402