Passed
Push — master ( e717c7...e31763 )
by
unknown
08:28 queued 13s
created

ValidationTokenController::validate()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 31
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 16
nc 6
nop 2
dl 0
loc 31
rs 9.7333
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Controller;
8
9
use Chamilo\CoreBundle\Entity\User;
10
use Chamilo\CoreBundle\Entity\ValidationToken;
11
use Chamilo\CoreBundle\Repository\Node\UserRepository;
12
use Chamilo\CoreBundle\Repository\TicketRelUserRepository;
13
use Chamilo\CoreBundle\Repository\TicketRepository;
14
use Chamilo\CoreBundle\Repository\TrackEDefaultRepository;
15
use Chamilo\CoreBundle\Repository\ValidationTokenRepository;
16
use Chamilo\CoreBundle\ServiceHelper\ValidationTokenHelper;
17
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
18
use Symfony\Component\HttpFoundation\RequestStack;
19
use Symfony\Component\HttpFoundation\Response;
20
use Symfony\Component\Routing\Annotation\Route;
21
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
22
use \Symfony\Component\Routing\Generator\UrlGeneratorInterface;
23
24
#[Route('/validate')]
25
class ValidationTokenController extends AbstractController
26
{
27
    public function __construct(
28
        private readonly ValidationTokenHelper $validationTokenHelper,
29
        private readonly ValidationTokenRepository $tokenRepository,
30
        private readonly TrackEDefaultRepository $trackEDefaultRepository,
31
        private readonly TicketRepository $ticketRepository,
32
        private readonly UserRepository $userRepository,
33
        private readonly TicketRelUserRepository $ticketRelUserRepository,
34
        private readonly TokenStorageInterface $tokenStorage,
35
        private readonly RequestStack $requestStack
36
    ) {}
37
38
    #[Route('/{type}/{hash}', name: 'chamilo_core_validate_token')]
39
    public function validate(string $type, string $hash): Response
40
    {
41
        $userId = $this->requestStack->getCurrentRequest()->query->get('user_id');
42
        $userId = $userId !== null ? (int) $userId : null;
43
44
        $token = $this->tokenRepository->findOneBy([
45
            'type' => $this->validationTokenHelper->getTypeId($type),
46
            'hash' => $hash
47
        ]);
48
49
        if (!$token) {
50
            throw $this->createNotFoundException('Invalid token.');
51
        }
52
53
        // Process the action related to the token type
54
        $this->processAction($token, $userId);
55
56
        // Remove the used token
57
        $this->tokenRepository->remove($token, true);
58
59
        // Register the token usage event
60
        $this->registerTokenUsedEvent($token);
61
62
        if ('ticket' === $type) {
63
            $ticketId = $token->getResourceId();
64
            return $this->redirect('/main/ticket/ticket_details.php?ticket_id=' . $ticketId);
65
        }
66
67
        return $this->render('@ChamiloCore/Validation/success.html.twig', [
68
            'type' => $type,
69
        ]);
70
    }
71
72
    #[Route('/test/generate-token/{type}/{resourceId}', name: 'test_generate_token')]
73
    public function testGenerateToken(string $type, int $resourceId): Response
74
    {
75
        $typeId = $this->validationTokenHelper->getTypeId($type);
76
        $token = new ValidationToken($typeId, $resourceId);
77
        $this->tokenRepository->save($token, true);
78
79
        $validationLink = $this->generateUrl('validate_token', [
80
            'type' => $type,
81
            'hash' => $token->getHash(),
82
        ], UrlGeneratorInterface::ABSOLUTE_URL);
83
84
        return new Response("Generated token: {$token->getHash()}<br>Validation link: <a href='{$validationLink}'>{$validationLink}</a>");
85
    }
86
87
    /**
88
     * Processes the action associated with the given token type.
89
     */
90
    private function processAction(ValidationToken $token, ?int $userId): void
91
    {
92
        switch ($token->getType()) {
93
            case ValidationTokenHelper::TYPE_TICKET:
94
                $this->unsubscribeUserFromTicket($token->getResourceId(), $userId);
95
                break;
96
            default:
97
                throw new \InvalidArgumentException('Unrecognized token type');
98
        }
99
    }
100
101
    /**
102
     * Unsubscribes a user from a ticket.
103
     */
104
    private function unsubscribeUserFromTicket(int $ticketId, ?int $userId): void
105
    {
106
        if (!$userId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $userId of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
107
            throw $this->createAccessDeniedException('User not authenticated.');
108
        }
109
110
        $ticket = $this->ticketRepository->find($ticketId);
111
        $user = $this->userRepository->find($userId);
112
113
        if ($ticket && $user) {
114
            $this->ticketRelUserRepository->unsubscribeUserFromTicket($user, $ticket);
115
            $this->trackEDefaultRepository->registerTicketUnsubscribeEvent($ticketId, $userId);
116
        } else {
117
            throw $this->createNotFoundException('Ticket or User not found.');
118
        }
119
    }
120
121
    /**
122
     * Registers the usage event of a validation token.
123
     */
124
    private function registerTokenUsedEvent(ValidationToken $token): void
125
    {
126
        $userId = $this->getUserId();
127
        $this->trackEDefaultRepository->registerTokenUsedEvent($token, $userId);
128
    }
129
130
    /**
131
     * Retrieves the current authenticated user's ID.
132
     */
133
    private function getUserId(): ?int
134
    {
135
        $user = $this->tokenStorage->getToken()?->getUser();
136
        return $user instanceof User ? $user->getId() : null;
137
    }
138
}
139