Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
96.23% covered (success)
96.23%
51 / 53
4.55% covered (danger)
4.55%
1 / 22
6.67% covered (danger)
6.67%
1 / 15
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
EventsListener
96.23% covered (success)
96.23%
51 / 53
4.55% covered (danger)
4.55%
1 / 22
6.67% covered (danger)
6.67%
1 / 15
80.00% covered (warning)
80.00%
4 / 5
129.08
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 responseEvent
100.00% covered (success)
100.00%
36 / 36
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 4
100.00% covered (success)
100.00%
1 / 1
3
 loginSuccessEvent
100.00% covered (success)
100.00%
3 / 3
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 4
100.00% covered (success)
100.00%
1 / 1
3
 loginFailureEvent
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 checkPassportEvent
100.00% covered (success)
100.00%
10 / 10
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 4
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3namespace App\Security;
4
5use App\Application;
6use Psr\Log\LoggerInterface;
7use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
8use Symfony\Component\HttpKernel\Event\ResponseEvent;
9use Symfony\Component\Security\Http\Event\CheckPassportEvent;
10use Symfony\Component\Security\Http\Event\LoginFailureEvent;
11use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
12
13class EventsListener
14{
15    protected Application $application;
16    protected LoggerInterface $logger;
17
18    public function __construct(Application $application, LoggerInterface $applicationLogger)
19    {
20        $this->application = $application;
21        $this->logger = $applicationLogger;
22    }
23
24    #[AsEventListener()]
25    public function responseEvent(ResponseEvent $event): void
26    {
27        // add security header
28        $response = $event->getResponse();
29        if ($this->application->isServerLocal()) {
30            $response->headers->set(
31                'Content-Security-Policy',
32                "default-src 'none'; " .
33                "script-src 'self' data: 'unsafe-inline' 'unsafe-hashes' 'unsafe-eval';" .
34                "script-src-elem 'self' data: 'unsafe-inline' 'unsafe-hashes' 'unsafe-eval';" .
35                "script-src-attr 'self' data: 'unsafe-inline' 'unsafe-hashes' 'unsafe-eval';" .
36                "style-src 'self' 'unsafe-inline';" .
37                "connect-src 'self'  *.etat-ge.ch ge.ch *.ge.ch;" .
38                "font-src 'self';" .
39                "media-src 'self';" .
40                "form-action 'self' *.etat-ge.ch ge.ch *.ge.ch *.geneveid.ch *.localhost;" .
41                "img-src 'self' data: ge.ch *.ge.ch *.etat-ge.ch ;"
42            );
43        } else {
44            $response->headers->set(
45                'Content-Security-Policy',
46                "default-src 'none'; " .
47                "script-src 'self' data: 'unsafe-inline' 'unsafe-hashes' 'unsafe-eval';" .
48                "script-src-elem 'self' data: 'unsafe-inline' 'unsafe-hashes' 'unsafe-eval';" .
49                "script-src-attr 'self' data: 'unsafe-inline' 'unsafe-hashes' 'unsafe-eval';" .
50                "style-src 'self' 'unsafe-inline';" .
51                "connect-src 'self'  *.etat-ge.ch ge.ch *.ge.ch;" .
52                "font-src 'self';" .
53                "media-src 'self';" .
54                "form-action 'self' *.etat-ge.ch ge.ch *.ge.ch *.geneveid.ch;" .
55                "img-src 'self' data: ge.ch *.ge.ch *.etat-ge.ch ;"
56            );
57        }
58        $response->headers->set('Cache-Control', 'max-age=0, must-revalidate, no-cache, no-store, private');
59        $response->headers->set('Expires', '0');
60        if ($this->application->isServerLocal()) {
61            // local server (no proxy), add some header for simulate the proxy
62            $response->headers->set('X-Frame-Options', 'SAMEORIGIN');
63        }
64        $response->headers->set('Referrer-Policy', 'strict-origin');
65        $response->headers->set('X-Content-Type-Options', 'nosniff');
66        $response->headers->set('Referrer-Policy', 'strict-origin');
67        $response->headers->set('X-XSS-Protection', '1; mode=block');
68    }
69
70    #[AsEventListener()]
71    public function loginSuccessEvent(LoginSuccessEvent $event): void
72    {
73        if (!$event->getPreviousToken() || (EnvAuthenticator::class !== get_class($event->getAuthenticator()))) {
74            /** @var User $user */
75            $user = $event->getUser();
76            $this->logger->info('Success login', $user->getAttributes());
77        }
78    }
79
80    #[AsEventListener()]
81    public function loginFailureEvent(LoginFailureEvent $event): void
82    {
83        /** @var ?User $user */
84        $user = $event->getPassport()?->getUser();
85        $this->logger->info('Login failure', $user ? $user->getAttributes() : []);
86    }
87
88    #[AsEventListener()]
89    public function checkPassportEvent(CheckPassportEvent $event): void
90    {
91        /**
92         * It is possible here to determine roles by other means such as db.
93         */
94        /** @var User $user */
95        $user = $event->getPassport()->getUser();
96        // guarantee every user at least has ROLE_USER
97        $roles = ['ROLE_USER' => 'ROLE_USER'];
98        $ginaRoles = $user->getGinaRoles();
99        $lenPrefixSamlRole = mb_strlen($this->application->getGinaRolePrefix());
100        foreach ($ginaRoles as $role) {
101            $role = mb_strtoupper(strval($role));
102            if (0 === strncmp($role, $this->application->getGinaRolePrefix(), $lenPrefixSamlRole)) {
103                $name = 'ROLE_' . mb_substr($role, $lenPrefixSamlRole);
104                $roles[$name] = $name;
105            }
106        }
107        $user->setRoles($roles);
108    }
109}