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

ServiceProvider::decryptAndVerifyAssertions()   B

Complexity

Conditions 11
Paths 49

Size

Total Lines 72
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 37
c 1
b 0
f 0
nc 49
nop 1
dl 0
loc 72
rs 7.3166

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