Completed
Push — master ( 8a36bf...4f5859 )
by Artem
09:40
created

EventListener/UserLockout.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
//----------------------------------------------------------------------
4
//
5
//  Copyright (C) 2017 Artem Rodygin
6
//
7
//  You should have received a copy of the MIT License along with
8
//  this file. If not, see <http://opensource.org/licenses/MIT>.
9
//
10
//----------------------------------------------------------------------
11
12
namespace Pignus\EventListener;
13
14
use Doctrine\Common\Persistence\ObjectManager;
15
use Pignus\Model\LockAccountTrait;
16
use Psr\Log\LoggerInterface;
17
use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
18
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
19
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
20
use Symfony\Component\HttpFoundation\RequestStack;
21
use Symfony\Component\Security\Core\Event\AuthenticationEvent;
22
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
23
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
24
use Symfony\Component\Security\Core\User\UserInterface;
25
26
/**
27
 * Locks/unlocks user in accordance with the authentication attempt.
28
 */
29
class UserLockout implements ContainerAwareInterface
30
{
31
    use ContainerAwareTrait;
32
33
    protected $logger;
34
    protected $request_stack;
35
    protected $firewalls;
36
    protected $manager;
37
    protected $auth_failures;
38
    protected $lock_duration;
39
40
    /**
41
     * Dependency Injection constructor.
42
     *
43
     * @param LoggerInterface $logger
44
     * @param RequestStack    $request_stack
45
     * @param FirewallMap     $firewalls
46
     * @param ObjectManager   $manager
47
     * @param int             $auth_failures
48
     * @param int             $lock_duration
49
     */
50 6
    public function __construct(
51
        LoggerInterface       $logger,
52
        RequestStack          $request_stack,
53
        FirewallMap           $firewalls,
54
        ObjectManager         $manager,
55
        int                   $auth_failures = null,
56
        int                   $lock_duration = null)
57
    {
58 6
        $this->logger        = $logger;
59 6
        $this->request_stack = $request_stack;
60 6
        $this->firewalls     = $firewalls;
61 6
        $this->manager       = $manager;
62 6
        $this->auth_failures = $auth_failures;
63 6
        $this->lock_duration = $lock_duration;
64 6
    }
65
66
    /**
67
     * Callback for successful authentication event.
68
     *
69
     * @param AuthenticationEvent $event
70
     */
71 2
    public function onSuccess(AuthenticationEvent $event)
72
    {
73 2
        if ($this->auth_failures === null) {
74 1
            return;
75
        }
76
77 1
        $token = $event->getAuthenticationToken();
78
79
        /** @var LockAccountTrait $user */
80 1
        $user = $token->getUser();
81
82 1
        if ($user instanceof UserInterface && in_array(LockAccountTrait::class, class_uses($user), true)) {
83
84 1
            $this->logger->info('Authentication success', [$user->getUsername()]);
85
86 1
            $user->unlockAccount();
0 ignored issues
show
The method unlockAccount() does not seem to exist on object<Symfony\Component...ore\User\UserInterface>.

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...
87
88 1
            $this->manager->persist($user);
89 1
            $this->manager->flush();
90
        }
91 1
    }
92
93
    /**
94
     * Callback for failed authentication event.
95
     *
96
     * @param AuthenticationFailureEvent $event
97
     */
98 4
    public function onFailure(AuthenticationFailureEvent $event)
99
    {
100 4
        if ($this->auth_failures === null) {
101 1
            return;
102
        }
103
104
        // Retrieve failed username.
105 3
        $token = $event->getAuthenticationToken();
106
107 3
        $credentials = $token->getCredentials();
108 3
        $username    = $token->getUsername() ?: ($credentials['username'] ?? null);
109
110
        // Retrieve current user provider.
111 3
        $provider = null;
112
113 3
        if ($request = $this->request_stack->getCurrentRequest()) {
114 3
            if ($config = $this->firewalls->getFirewallConfig($request)) {
115 3
                if ($serviceId = $config->getProvider()) {
116 3
                    $provider = $this->container->get($serviceId);
117
                }
118
            }
119
        }
120
121
        // Try to fetch user entity.
122 3
        if ($username !== null && $provider !== null) {
123
124
            try {
125
                /** @var LockAccountTrait $user */
126 3
                $user = $provider->loadUserByUsername($username);
127
            }
128 1
            catch (UsernameNotFoundException $e) {
129 1
                $user = null;
130
            }
131
132 3
            if ($user instanceof UserInterface && in_array(LockAccountTrait::class, class_uses($user), true)) {
133
134 2
                $this->logger->info('Authentication failure', [$username]);
135
136 2
                if ($user->incAuthFailures() >= $this->auth_failures) {
137
138 2
                    if ($this->lock_duration === null) {
139 1
                        $user->lockAccount();
140
                    }
141
                    else {
142 1
                        $interval = sprintf('PT%dM', $this->lock_duration);
143 1
                        $user->lockAccount(date_create()->add(new \DateInterval($interval)));
144
                    }
145
                }
146
147 2
                $this->manager->persist($user);
148 2
                $this->manager->flush();
149
            }
150
        }
151 3
    }
152
}
153