ResponseValidator::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 2
b 0
f 0
nc 1
nop 3
dl 0
loc 8
rs 10
1
<?php
2
3
/**
4
 * Copyright 2023 SURFnet bv
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace Surfnet\StepupGateway\SecondFactorOnlyBundle\Service\Gateway;
20
21
use Surfnet\SamlBundle\Http\PostBinding;
22
use Surfnet\StepupBundle\Service\SecondFactorTypeService;
23
use Surfnet\StepupBundle\Value\SecondFactorType;
24
use Surfnet\StepupGateway\GatewayBundle\Service\SecondFactor\SecondFactorInterface;
25
use Surfnet\StepupGateway\SamlStepupProviderBundle\Provider\ProviderRepository;
26
use Surfnet\StepupGateway\SecondFactorOnlyBundle\Exception\ReceivedInvalidSubjectNameIdException;
27
use Symfony\Component\HttpFoundation\Request;
28
29
class ResponseValidator
30
{
31
    /** @var SecondFactorTypeService */
32
    private $secondFactorTypeService;
33
34
    /** @var ProviderRepository */
35
    private $providerRepository;
36
37
    /** @var PostBinding  */
38
    private $postBinding;
39
40
    public function __construct(
41
        SecondFactorTypeService $secondFactorTypeService,
42
        ProviderRepository $providerRepository,
43
        PostBinding $postBinding
44
    ) {
45
        $this->secondFactorTypeService = $secondFactorTypeService;
46
        $this->providerRepository = $providerRepository;
47
        $this->postBinding = $postBinding;
48
    }
49
50
    /**
51
     *
52
     */
53
    public function validate(Request $request, SecondFactorInterface $secondFactor, string $nameIdFromState): void
54
    {
55
        $secondFactorType = new SecondFactorType($secondFactor->getSecondFactorType());
56
        $hasSamlResponse = $request->request->has('SAMLResponse');
57
        // When dealing with a GSSP response. It is advised to receive the SAML response through POST Binding,
58
        // testing the preconditions.
59
        if ($hasSamlResponse && $this->secondFactorTypeService->isGssf($secondFactorType)) {
60
            $provider = $this->providerRepository->get($secondFactorType->getSecondFactorType());
61
            // Receive the response via POST Binding, this will test all the regular pre-conditions
62
            $samlResponse = $this->postBinding->processResponse(
63
                $request,
64
                $provider->getRemoteIdentityProvider(),
65
                $provider->getServiceProvider()
66
            );
67
            $subjectNameIdFromResponse = $samlResponse->getNameId()->getValue();
68
            // Additionally test if the name id from the GSSP matches the SF identifier that we have in state
69
            if ($subjectNameIdFromResponse !== $secondFactor->getSecondFactorIdentifier()) {
70
                throw new ReceivedInvalidSubjectNameIdException(
71
                    sprintf(
72
                        'The nameID received from the GSSP (%s) did not match the selected second factor (%s). This '.
73
                        'might be an indication someone is tampering with a GSSP. The authentication was started by %s',
74
                        $subjectNameIdFromResponse,
75
                        $secondFactor->getSecondFactorIdentifier(),
76
                        $nameIdFromState
77
                    )
78
                );
79
            }
80
        }
81
    }
82
}
83