Passed
Push — master ( 719760...59ab2e )
by Angel Fernando Quiroz
18:35
created

AnonymousUserSubscriber::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 4
dl 0
loc 6
rs 10
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
declare(strict_types=1);
6
7
namespace Chamilo\CoreBundle\EventSubscriber;
8
9
use Chamilo\CoreBundle\Entity\TrackELogin;
10
use Chamilo\CoreBundle\Entity\User;
11
use Chamilo\CoreBundle\Settings\SettingsManager;
12
use DateTime;
13
use Doctrine\ORM\EntityManagerInterface;
14
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15
use Symfony\Component\HttpFoundation\Session\SessionInterface;
16
use Symfony\Component\HttpKernel\Event\RequestEvent;
17
use Symfony\Component\HttpKernel\KernelEvents;
18
use Symfony\Component\Security\Core\Security;
19
20
class AnonymousUserSubscriber implements EventSubscriberInterface
21
{
22
    private const MAX_ANONYMOUS_USERS = 5;
23
    private Security $security;
24
    private EntityManagerInterface $entityManager;
25
    private SessionInterface $session;
26
    private SettingsManager $settingsManager;
27
28
    public function __construct(Security $security, EntityManagerInterface $entityManager, SessionInterface $session, SettingsManager $settingsManager)
29
    {
30
        $this->security = $security;
31
        $this->entityManager = $entityManager;
32
        $this->session = $session;
33
        $this->settingsManager = $settingsManager;
34
    }
35
36
    public function onKernelRequest(RequestEvent $event): void
37
    {
38
        if (null !== $this->security->getUser()) {
39
            return;
40
        }
41
42
        $request = $event->getRequest();
43
        $userIp = $request->getClientIp();
44
45
        $anonymousUserId = $this->getOrCreateAnonymousUserId($userIp);
46
        if (null !== $anonymousUserId) {
47
            $trackLoginRepository = $this->entityManager->getRepository(TrackELogin::class);
48
49
            // Check if a login record already exists for this user and IP
50
            $existingLogin = $trackLoginRepository->findOneBy(['userIp' => $userIp, 'user' => $anonymousUserId]);
51
            if (!$existingLogin) {
52
                // Record the access if it does not exist
53
                $trackLogin = new TrackELogin();
54
                $trackLogin->setUserIp($userIp)
55
                    ->setLoginDate(new DateTime())
56
                    ->setUser($this->entityManager->getReference(User::class, $anonymousUserId))
57
                ;
58
59
                $this->entityManager->persist($trackLogin);
60
                $this->entityManager->flush();
61
            }
62
63
            $userRepository = $this->entityManager->getRepository(User::class);
64
            $user = $userRepository->find($anonymousUserId);
65
66
            if ($user) {
67
                // Store user information in the session
68
                $userInfo = [
69
                    'user_id' => $user->getId(),
70
                    'username' => $user->getUsername(),
71
                    'firstname' => $user->getFirstname(),
72
                    'lastname' => $user->getLastname(),
73
                    'firstName' => $user->getFirstname(),
74
                    'lastName' => $user->getLastname(),
75
                    'email' => $user->getEmail(),
76
                    'official_code' => $user->getOfficialCode(),
77
                    'picture_uri' => $user->getPictureUri(),
78
                    'status' => $user->getStatus(),
79
                    'active' => $user->getActive(),
80
                    'auth_source' => $user->getAuthSource(),
81
                    'theme' => $user->getTheme(),
82
                    'language' => $user->getLocale(),
83
                    'registration_date' => $user->getRegistrationDate()->format('Y-m-d H:i:s'),
84
                    'expiration_date' => $user->getExpirationDate() ? $user->getExpirationDate()->format('Y-m-d H:i:s') : null,
85
                    'last_login' => $user->getLastLogin() ? $user->getLastLogin()->format('Y-m-d H:i:s') : null,
86
                    'is_anonymous' => true,
87
                ];
88
89
                $this->session->set('_user', $userInfo);
90
            }
91
        }
92
    }
93
94
    public static function getSubscribedEvents()
95
    {
96
        return [
97
            KernelEvents::REQUEST => 'onKernelRequest',
98
        ];
99
    }
100
101
    private function getOrCreateAnonymousUserId(string $userIp): ?int
102
    {
103
        $userRepository = $this->entityManager->getRepository(User::class);
104
        $trackLoginRepository = $this->entityManager->getRepository(TrackELogin::class);
105
106
        $maxAnonymousUsers = (int) $this->settingsManager->getSetting('admin.max_anonymous_users');
107
        if (0 === $maxAnonymousUsers) {
108
            $maxAnonymousUsers = self::MAX_ANONYMOUS_USERS;
109
        }
110
        $anonymousUsers = $userRepository->findBy(['status' => User::ANONYMOUS], ['registrationDate' => 'ASC']);
111
112
        // Check in TrackELogin if there is an anonymous user with the same IP
113
        foreach ($anonymousUsers as $user) {
114
            $loginRecord = $trackLoginRepository->findOneBy(['userIp' => $userIp, 'user' => $user]);
115
            if ($loginRecord) {
116
                return $user->getId();
117
            }
118
        }
119
120
        // Delete excess anonymous users
121
        while (\count($anonymousUsers) >= $maxAnonymousUsers) {
122
            $oldestAnonymousUser = array_shift($anonymousUsers);
123
            if ($oldestAnonymousUser) {
124
                error_log('Deleting oldest anonymous user: '.$oldestAnonymousUser->getId());
125
                $this->entityManager->remove($oldestAnonymousUser);
126
                $this->entityManager->flush();
127
            }
128
        }
129
130
        // Create a new anonymous user
131
        $uniqueId = uniqid();
132
        $anonymousUser = (new User())
133
            ->setSkipResourceNode(true)
134
            ->setLastname('Joe')
135
            ->setFirstname('Anonymous')
136
            ->setUsername('anon_'.$uniqueId)
137
            ->setStatus(User::ANONYMOUS)
138
            ->setPlainPassword('anon')
139
            ->setEmail('anon_'.$uniqueId.'@localhost.local')
140
            ->setOfficialCode('anonymous')
141
            ->setCreatorId(1)
142
            ->addRole('ROLE_ANONYMOUS')
143
        ;
144
145
        $this->entityManager->persist($anonymousUser);
146
        $this->entityManager->flush();
147
148
        return $anonymousUser->getId();
149
    }
150
}
151