Passed
Push — master ( 26f979...52fc9a )
by
unknown
11:08 queued 03:38
created

AccountController::edit()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 36
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 22
c 1
b 0
f 0
nc 4
nop 4
dl 0
loc 36
rs 8.9457
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\Form\ChangePasswordType;
11
use Chamilo\CoreBundle\Form\ProfileType;
12
use Chamilo\CoreBundle\Repository\Node\IllustrationRepository;
13
use Chamilo\CoreBundle\Repository\Node\UserRepository;
14
use Chamilo\CoreBundle\ServiceHelper\UserHelper;
15
use Chamilo\CoreBundle\Settings\SettingsManager;
16
use Chamilo\CoreBundle\Traits\ControllerTrait;
17
use Security;
18
use Symfony\Component\HttpFoundation\RedirectResponse;
19
use Symfony\Component\HttpFoundation\Request;
20
use Symfony\Component\HttpFoundation\Response;
21
use Symfony\Component\Routing\Attribute\Route;
22
use Symfony\Component\Security\Core\User\UserInterface;
23
use Symfony\Component\Form\FormError;
24
use Symfony\Component\Security\Csrf\CsrfToken;
25
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
26
use Symfony\Contracts\Translation\TranslatorInterface;
27
28
/**
29
 * @author Julio Montoya <[email protected]>
30
 */
31
#[Route('/account')]
32
class AccountController extends BaseController
33
{
34
    use ControllerTrait;
35
36
    public function __construct(
37
        private readonly UserHelper $userHelper,
38
        private readonly TranslatorInterface $translator
39
    ) {}
40
41
    #[Route('/edit', name: 'chamilo_core_account_edit', methods: ['GET', 'POST'])]
42
    public function edit(Request $request, UserRepository $userRepository, IllustrationRepository $illustrationRepo, SettingsManager $settingsManager): Response
43
    {
44
        $user = $this->userHelper->getCurrent();
45
46
        if (!\is_object($user) || !$user instanceof UserInterface) {
47
            throw $this->createAccessDeniedException('This user does not have access to this section');
48
        }
49
50
        /** @var User $user */
51
        $form = $this->createForm(ProfileType::class, $user);
52
        $form->setData($user);
53
        $form->handleRequest($request);
54
55
        if ($form->isSubmitted() && $form->isValid()) {
56
            $illustration = $form['illustration']->getData();
57
            if ($illustration) {
58
                $illustrationRepo->deleteIllustration($user);
59
                $illustrationRepo->addIllustration($user, $user, $illustration);
60
            }
61
62
            $showTermsIfProfileCompleted = ('true' === $settingsManager->getSetting('show_terms_if_profile_completed'));
63
            $user->setProfileCompleted($showTermsIfProfileCompleted);
64
65
            $userRepository->updateUser($user);
66
            $this->addFlash('success', $this->trans('Updated'));
67
            $url = $this->generateUrl('chamilo_core_account_home');
68
69
            $request->getSession()->set('_locale_user', $user->getLocale());
70
71
            return new RedirectResponse($url);
72
        }
73
74
        return $this->render('@ChamiloCore/Account/edit.html.twig', [
75
            'form' => $form,
76
            'user' => $user,
77
        ]);
78
    }
79
80
    #[Route('/change-password', name: 'chamilo_core_account_change_password', methods: ['GET', 'POST'])]
81
    public function changePassword(Request $request, UserRepository $userRepository, CsrfTokenManagerInterface $csrfTokenManager): Response
82
    {
83
        $user = $this->getUser();
84
85
        if (!\is_object($user) || !$user instanceof UserInterface) {
86
            throw $this->createAccessDeniedException('This user does not have access to this section');
87
        }
88
89
        $form = $this->createForm(ChangePasswordType::class);
90
        $form->handleRequest($request);
91
92
        if ($form->isSubmitted() && $form->isValid()) {
93
            $submittedToken = $request->request->get('_token');
94
95
            if (!$csrfTokenManager->isTokenValid(new CsrfToken('change_password', $submittedToken))) {
96
                $form->addError(new FormError($this->translator->trans('CSRF token is invalid. Please try again.')));
97
            } else {
98
                $currentPassword = $form->get('currentPassword')->getData();
99
                $newPassword = $form->get('newPassword')->getData();
100
                $confirmPassword = $form->get('confirmPassword')->getData();
101
102
                if (!$userRepository->isPasswordValid($user, $currentPassword)) {
103
                    $form->get('currentPassword')->addError(new FormError($this->translator->trans('Current password is incorrect.')));
104
                } elseif ($newPassword !== $confirmPassword) {
105
                    $form->get('confirmPassword')->addError(new FormError($this->translator->trans('Passwords do not match.')));
106
                } else {
107
                    $errors = $this->validatePassword($newPassword);
108
                    if (count($errors) > 0) {
109
                        foreach ($errors as $error) {
110
                            $form->get('newPassword')->addError(new FormError($error));
111
                        }
112
                    } else {
113
                        $user->setPlainPassword($newPassword);
114
                        $userRepository->updateUser($user);
115
                        $this->addFlash('success', $this->translator->trans('Password changed successfully.'));
116
                        return $this->redirectToRoute('chamilo_core_account_home');
117
                    }
118
                }
119
            }
120
        }
121
122
        return $this->render('@ChamiloCore/Account/change_password.html.twig', [
123
            'form' => $form->createView(),
124
        ]);
125
    }
126
127
    /**
128
     * Validate the password against the same requirements as the client-side validation.
129
     */
130
    private function validatePassword(string $password): array
131
    {
132
        $errors = [];
133
        $minRequirements = Security::getPasswordRequirements()['min'];
134
135
        if (strlen($password) < $minRequirements['length']) {
136
            $errors[] = $this->translator->trans('Password must be at least %length% characters long.', ['%length%' => $minRequirements['length']]);
137
        }
138
        if ($minRequirements['lowercase'] > 0 && !preg_match('/[a-z]/', $password)) {
139
            $errors[] = $this->translator->trans('Password must contain at least %count% lowercase characters.', ['%count%' => $minRequirements['lowercase']]);
140
        }
141
        if ($minRequirements['uppercase'] > 0 && !preg_match('/[A-Z]/', $password)) {
142
            $errors[] = $this->translator->trans('Password must contain at least %count% uppercase characters.', ['%count%' => $minRequirements['uppercase']]);
143
        }
144
        if ($minRequirements['numeric'] > 0 && !preg_match('/[0-9]/', $password)) {
145
            $errors[] = $this->translator->trans('Password must contain at least %count% numerical (0-9) characters.', ['%count%' => $minRequirements['numeric']]);
146
        }
147
        if ($minRequirements['specials'] > 0 && !preg_match('/[\W]/', $password)) {
148
            $errors[] = $this->translator->trans('Password must contain at least %count% special characters.', ['%count%' => $minRequirements['specials']]);
149
        }
150
151
        return $errors;
152
    }
153
}
154