ShibbolethAuthenticationListener   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 13
dl 0
loc 148
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 1
D handle() 0 89 18
1
<?php
2
3
namespace Kuleuven\AuthenticationBundle\Security;
4
5
use Kuleuven\AuthenticationBundle\Service\ShibbolethServiceProvider;
6
use Kuleuven\AuthenticationBundle\Traits\LoggerTrait;
7
use Psr\Log\LoggerAwareInterface;
8
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
9
use Symfony\Component\HttpFoundation\Response;
10
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
11
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
12
use Symfony\Component\Security\Core\Role\SwitchUserRole;
13
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
14
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
15
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
16
use Symfony\Component\Security\Core\Exception\AuthenticationException;
17
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
18
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
19
use Symfony\Component\Security\Http\SecurityEvents;
20
21
class ShibbolethAuthenticationListener implements ListenerInterface, LoggerAwareInterface
22
{
23
    use LoggerTrait;
24
25
    /**
26
     * @var ShibbolethServiceProvider
27
     */
28
    protected $shibbolethServiceProvider;
29
30
    /**
31
     * @var AuthenticationManagerInterface
32
     */
33
    protected $authenticationManager;
34
35
    /**
36
     * @var AuthenticationEntryPointInterface
37
     */
38
    protected $authenticationEntryPoint;
39
40
    /**
41
     * @var TokenStorageInterface
42
     */
43
    protected $tokenStorage;
44
45
    /**
46
     * @var EventDispatcherInterface
47
     */
48
    protected $eventDispatcher;
49
50
    /**
51
     * @var array
52
     */
53
    protected $defaultRoles;
54
55
    /**
56
     * @var string
57
     */
58
    protected $providerKey;
59
60
    public function __construct(
61
        ShibbolethServiceProvider $shibbolethServiceProvider,
62
        TokenStorageInterface $tokenStorage,
63
        AuthenticationManagerInterface $authenticationManager,
64
        AuthenticationEntryPointInterface $authenticationEntryPoint = null,
65
        EventDispatcherInterface $eventDispatcher = null,
66
        array $defaultRoles = [],
67
        $providerKey = ''
68
    )
69
    {
70
        $this->shibbolethServiceProvider = $shibbolethServiceProvider;
71
        $this->tokenStorage = $tokenStorage;
72
        $this->providerKey = $providerKey;
73
        $this->authenticationManager = $authenticationManager;
74
        $this->authenticationEntryPoint = $authenticationEntryPoint;
75
        $this->eventDispatcher = $eventDispatcher;
76
        $this->defaultRoles = $defaultRoles;
77
    }
78
79
    public function handle(GetResponseEvent $event)
80
    {
81
        $request = $event->getRequest();
82
83
        $attributes = $this->shibbolethServiceProvider->getAttributes();
84
85
        if (empty($attributes)) {
86
            $this->log('Shibboleth attributes not found');
87
            return;
88
        }
89
90
        $this->log(sprintf('Shibboleth attributes found: %s', json_encode($attributes)));
91
92
        if (!$this->shibbolethServiceProvider->isAuthenticated()) {
93
            $this->log('Shibboleth has not authenticated your request.');
94
            return;
95
        }
96
97
        $username = $this->shibbolethServiceProvider->getUsername();
98
99
        if (empty($username)) {
100
            $this->log('Username not found');
101
            return;
102
        }
103
104
        $this->log(sprintf('Username found: %s', $username));
105
        $token = $this->tokenStorage->getToken();
106
107
        if (!empty($token)) {
108
            if ($token instanceof KuleuvenUserToken && $token->isAuthenticated()) {
109
                $this->log(sprintf('Token found: %s', $token));
110
                if ($token->getUsername() === $username && count($token->getRoles()) === count($token->getUser()->getRoles())) {
111
                    $this->log(sprintf('Token authenticated for username "%s": %s', $username, $token));
112
                    return;
113
                }
114
                $roles = $token->getRoles();
115
                foreach ($roles as $role) {
116
                    if ($role instanceof SwitchUserRole) {
117
                        if ($role->getSource()->getUser()->getUsername() === $username) {
118
                            $this->log(sprintf('Token authenticated for username "%s", impersonating "%s": %s', $username, $token->getUsername(), $token));
119
                            return;
120
                        }
121
                        break;
122
                    }
123
                }
124
            }
125
        }
126
127
        try {
128
            $token = new KuleuvenUserToken(
129
                $username,
130
                $attributes,
131
                $this->providerKey,
132
                $this->defaultRoles
133
            );
134
            $this->log(sprintf('Token created for username "%s": %s', $username, $token));
135
136
            $authenticationToken = $this->authenticationManager->authenticate($token);
137
            if ($authenticationToken instanceof TokenInterface) {
138
                $this->log(sprintf('Set authentication token: %s', $authenticationToken));
139
                $this->tokenStorage->setToken($authenticationToken);
140
                if (null !== $this->eventDispatcher) {
141
                    $loginEvent = new InteractiveLoginEvent($request, $authenticationToken);
142
                    $this->log('Dispatch login event');
143
                    $this->eventDispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
144
                }
145
            } elseif ($authenticationToken instanceof Response) {
146
                $this->log('Using authentication token as response...');
147
                $event->setResponse($authenticationToken);
148
            }
149
        } catch (AuthenticationException $failed) {
150
            $this->log(sprintf('Authentication request failed for username "%s": %s', $username, $failed->getMessage()));
151
152
            $token = $this->tokenStorage->getToken();
153
            if ($token instanceof KuleuvenUserToken) {
154
                $this->tokenStorage->setToken(null);
155
                $this->log(sprintf('Token removed from storage', $token));
156
            }
157
158
            try {
159
                $event->setResponse($this->authenticationEntryPoint->start($request, $failed));
160
            } catch (AuthenticationException $failed) {
161
                $this->log('Entry point failed, sending forbidden response...');
162
                $response = (new Response());
163
                $response->setStatusCode(Response::HTTP_FORBIDDEN);
164
                $event->setResponse($response);
165
            }
166
        }
167
    }
168
}
169