Passed
Push — master ( c4afc2...9cde23 )
by Pieter van der
27:49 queued 12:42
created

ProcessSamlAuthenticationHandler::process()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 38
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 5
eloc 20
nc 4
nop 1
dl 0
loc 38
rs 9.2888
c 2
b 0
f 0
1
<?php
2
3
/**
4
 * Copyright 2016 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\StepupSelfService\SelfServiceBundle\Security\Authentication\Handler;
20
21
use Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger;
22
use Surfnet\SamlBundle\SAML2\Response\Assertion\InResponseTo;
23
use Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\AuthenticatedSessionStateHandler;
24
use Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\SamlAuthenticationStateHandler;
25
use Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\SamlInteractionProvider;
26
use Surfnet\StepupSelfService\SelfServiceBundle\Security\Authentication\Token\SamlToken;
27
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
28
use Symfony\Component\HttpFoundation\RedirectResponse;
29
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
30
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
31
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
32
use Symfony\Component\Security\Core\Exception\AuthenticationException;
33
34
/**
35
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects) SamlResponse parsing, validation authentication and error handling
36
 *                                                 requires quite a few classes as it is fairly complex.
37
 */
38
class ProcessSamlAuthenticationHandler implements AuthenticationHandler
39
{
40
    /**
41
     * @var AuthenticationHandler
42
     */
43
    private $nextHandler;
44
45
    /**
46
     * @var TokenStorageInterface
47
     */
48
    private $tokenStorage;
49
50
    /**
51
     * @var SamlInteractionProvider
52
     */
53
    private $samlInteractionProvider;
54
55
    /**
56
     * @var SamlAuthenticationStateHandler
57
     */
58
    private $authenticationStateHandler;
59
60
    /**
61
     * @var AuthenticatedSessionStateHandler
62
     */
63
    private $authenticatedSession;
64
65
    /**
66
     * @var AuthenticationManagerInterface
67
     */
68
    private $authenticationManager;
69
70
    /**
71
     * @var SamlAuthenticationLogger
72
     */
73
    private $authenticationLogger;
74
75
    /**
76
     * @var EngineInterface
77
     */
78
    private $templating;
79
80
    public function __construct(
81
        TokenStorageInterface $tokenStorage,
82
        SamlInteractionProvider $samlInteractionProvider,
83
        SamlAuthenticationStateHandler $authenticationStateHandler,
84
        AuthenticatedSessionStateHandler $authenticatedSession,
85
        AuthenticationManagerInterface $authenticationManager,
86
        SamlAuthenticationLogger $authenticationLogger,
87
        EngineInterface $templating
88
    ) {
89
        $this->tokenStorage               = $tokenStorage;
90
        $this->samlInteractionProvider    = $samlInteractionProvider;
91
        $this->authenticationStateHandler = $authenticationStateHandler;
92
        $this->authenticatedSession       = $authenticatedSession;
93
        $this->authenticationManager      = $authenticationManager;
94
        $this->authenticationLogger       = $authenticationLogger;
95
        $this->templating                 = $templating;
96
    }
97
98
    public function process(GetResponseEvent $event)
99
    {
100
        if ($this->tokenStorage->getToken() === null
101
            && $this->samlInteractionProvider->isSamlAuthenticationInitiated()
102
        ) {
103
            $expectedInResponseTo = $this->authenticationStateHandler->getRequestId();
104
            $logger               = $this->authenticationLogger->forAuthentication($expectedInResponseTo);
105
106
            $logger->notice('No authenticated user and AuthnRequest pending, attempting to process SamlResponse');
107
108
            $assertion = $this->samlInteractionProvider->processSamlResponse($event->getRequest());
109
110
            if (!InResponseTo::assertEquals($assertion, $expectedInResponseTo)) {
111
                throw new AuthenticationException('Unknown or unexpected InResponseTo in SAMLResponse');
112
            }
113
114
            $logger->notice('Successfully processed SAMLResponse, attempting to authenticate');
115
116
            $token            = new SamlToken();
117
            $token->assertion = $assertion;
118
119
            $authToken = $this->authenticationManager->authenticate($token);
120
121
            $this->authenticatedSession->logAuthenticationMoment();
122
            $this->tokenStorage->setToken($authToken);
123
124
            // migrate the session to prevent session hijacking
125
            $this->authenticatedSession->migrate();
126
127
            $event->setResponse(new RedirectResponse($this->authenticatedSession->getCurrentRequestUri()));
128
129
            $logger->notice('Authentication succeeded, redirecting to original location');
130
131
            return;
132
        }
133
134
        if ($this->nextHandler) {
135
            $this->nextHandler->process($event);
136
        }
137
    }
138
139
    public function setNext(AuthenticationHandler $handler)
140
    {
141
        $this->nextHandler = $handler;
142
    }
143
}
144