Completed
Push — master ( d27d79...effeb0 )
by Craig
05:53
created

AccountController::lostUserNameAction()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 35
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 23
nc 6
nop 1
dl 0
loc 35
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the Zikula package.
5
 *
6
 * Copyright Zikula Foundation - http://zikula.org/
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Zikula\ZAuthModule\Controller;
13
14
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
15
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
16
use Symfony\Component\HttpFoundation\RedirectResponse;
17
use Symfony\Component\HttpFoundation\Request;
18
use Symfony\Component\HttpFoundation\Response;
19
use Symfony\Component\Routing\RouterInterface;
20
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
21
use Zikula\Bundle\HookBundle\Dispatcher\Exception\RuntimeException;
22
use Zikula\Core\Controller\AbstractController;
23
use Zikula\Core\Exception\FatalErrorException;
24
use Zikula\UsersModule\Constant as UsersConstant;
25
use Zikula\UsersModule\Entity\UserEntity;
26
use Zikula\ZAuthModule\Entity\UserVerificationEntity;
27
use Zikula\ZAuthModule\Form\Type\ChangeEmailType;
28
use Zikula\ZAuthModule\Form\Type\ChangePasswordType;
29
use Zikula\ZAuthModule\Form\Type\LostPasswordType;
30
use Zikula\ZAuthModule\Form\Type\LostUserNameType;
31
use Zikula\ZAuthModule\ZAuthConstant;
32
33
/**
34
 * @Route("/account")
35
 */
36
class AccountController extends AbstractController
37
{
38
    /**
39
     * @Route("/lost-user-name")
40
     * @Template
41
     * @param Request $request
42
     * @return array|RedirectResponse
43
     */
44
    public function lostUserNameAction(Request $request)
45
    {
46
        if ($this->get('zikula_users_module.current_user')->isLoggedIn()) {
47
            return $this->redirectToRoute('zikulausersmodule_account_menu');
48
        }
49
50
        $form = $this->createForm(LostUserNameType::class, [], [
51
            'translator' => $this->get('translator.default')
52
        ]);
53
        $form->handleRequest($request);
54
        if ($form->isSubmitted()) {
55
            $data = $form->getData();
56
            $mapping = $this->get('zikula_zauth_module.authentication_mapping_repository')->findBy(['email' => $data['email']]);
57
            if (count($mapping) == 1) {
58
                // send email
59
                $sent = $this->get('zikula_zauth_module.helper.mail_helper')->sendNotification($mapping[0]->getEmail(), 'lostuname', [
60
                    'uname' => $mapping[0]->getUname(),
61
                    'requestedByAdmin' => false,
62
                ]);
63
                if ($sent) {
64
                    $this->addFlash('status', $this->__f('Done! The account information for %s has been sent via e-mail.', ['%s' => $data['email']]));
65
                } else {
66
                    $this->addFlash('error', $this->__('Unable to send email to the requested address. Please contact the system administrator for assistance.'));
67
                }
68
            } elseif (count($mapping) > 1) {
69
                $this->addFlash('error', $this->__('There are too many users registered with that address. Please contact the system administrator for assistance.'));
70
            } else {
71
                $this->addFlash('error', $this->__('Unable to send email to the requested address. Please contact the system administrator for assistance.'));
72
            }
73
        }
74
75
        return [
76
            'form' => $form->createView(),
77
        ];
78
    }
79
80
    /**
81
     * @Route("/lost-password")
82
     * @Template
83
     * @param Request $request
84
     * @return array|RedirectResponse
85
     */
86
    public function lostPasswordAction(Request $request)
87
    {
88
        if ($this->get('zikula_users_module.current_user')->isLoggedIn()) {
89
            return $this->redirectToRoute('zikulausersmodule_account_menu');
90
        }
91
92
        $form = $this->createForm(LostPasswordType::class, [], [
93
            'translator' => $this->get('translator.default')
94
        ]);
95
        $form->handleRequest($request);
96
        if ($form->isSubmitted() && $form->isValid()) {
97
            $redirectToRoute = '';
98
            $map = ['uname' => $this->__('username'), 'email' => $this->__('email address')];
99
            $data = $form->getData();
100
            $field = empty($data['uname']) ? 'email' : 'uname';
101
            $inverse = $field == 'uname' ? 'email' : 'uname';
102
            $mapping = $this->get('zikula_zauth_module.authentication_mapping_repository')->findBy([$field => $data[$field]]);
103
            if (count($mapping) == 1) {
104
                $mapping = $mapping[0];
105
                $user = $this->get('zikula_users_module.user_repository')->find($mapping->getUid());
106
                switch ($user->getActivated()) {
107
                    case UsersConstant::ACTIVATED_ACTIVE:
108
                        $changePasswordExpireDays = $this->getVar(ZAuthConstant::MODVAR_EXPIRE_DAYS_CHANGE_PASSWORD, ZAuthConstant::DEFAULT_EXPIRE_DAYS_CHANGE_PASSWORD);
109
                        $lostPasswordId = $this->get('zikula_zauth_module.helper.lost_password_verification_helper')->createLostPasswordId($mapping);
110
                        $sent = $this->get('zikula_zauth_module.helper.mail_helper')->sendNotification($mapping->getEmail(), 'lostpassword', [
111
                            'uname' => $mapping->getUname(),
112
                            'validDays' => $changePasswordExpireDays,
113
                            'lostPasswordId' => $lostPasswordId,
114
                            'requestedByAdmin' => false,
115
                        ]);
116
                        if ($sent) {
117
                            $this->addFlash('status', $this->__f('Done! The confirmation link for %s has been sent via e-mail.', ['%s' => $data[$field]]));
118
                            $redirectToRoute = 'zikulausersmodule_account_menu';
119
                        } else {
120
                            $this->addFlash('error', $this->__f('Unable to send email to the requested %s. Please try your %o or contact the system administrator for assistance.', ['%s' => $map[$field], '%o' => $map[$inverse]]));
121
                        }
122
                        break;
123
                    case UsersConstant::ACTIVATED_INACTIVE:
124
                        if ($this->getVar(UsersConstant::MODVAR_LOGIN_DISPLAY_INACTIVE_STATUS, UsersConstant::DEFAULT_LOGIN_DISPLAY_INACTIVE_STATUS)) {
125
                            $this->addFlash('error', $this->__('Sorry! Your account is marked as inactive. Please contact a site administrator for more information.'));
126
                        }
127
                        $redirectToRoute = 'zikulausersmodule_account_menu';
128
                        break;
129
                    case UsersConstant::ACTIVATED_PENDING_REG:
130
                        $displayPendingApproval = $this->getVar(UsersConstant::MODVAR_LOGIN_DISPLAY_APPROVAL_STATUS, UsersConstant::DEFAULT_LOGIN_DISPLAY_APPROVAL_STATUS);
131
                        $displayPendingVerification = $this->getVar(UsersConstant::MODVAR_LOGIN_DISPLAY_VERIFY_STATUS, UsersConstant::DEFAULT_LOGIN_DISPLAY_VERIFY_STATUS);
132
                        if ($displayPendingApproval || $displayPendingVerification) {
133
                            $this->addFlash('error', $this->__('Sorry! Your account has not completed the registration process. Please contact a site administrator for more information.'));
134
                            $redirectToRoute = 'zikulausersmodule_account_menu';
135
                        } else {
136
                            $this->addFlash('error', $this->__('Sorry! An account could not be located with that information. Correct your entry and try again. If you have recently registered a new account with this site, we may be waiting for you to verify your e-mail address, or we might not have approved your registration request yet.'));
137
                        }
138
                        break;
139
                    default:
140
                        $this->addFlash('error', $this->__('Sorry! An active account could not be located with that information. Correct your entry and try again. If you have recently registered a new account with this site, we may be waiting for you to verify your e-mail address, or we might not have approved your registration request yet.'));
141
                }
142
            } elseif (count($mapping) > 1) {
143
                $this->addFlash('error', $this->__('There are too many users registered with that address. Please contact the system administrator for assistance.'));
144
            } else {
145
                $this->addFlash('error', $this->__f('%s not found. Please contact the system administrator for assistance.', ['%s' => ucwords($map[$field])]));
146
            }
147
            if (!empty($redirectToRoute)) {
148
                return $this->redirectToRoute($redirectToRoute);
149
            }
150
        }
151
152
        return [
153
            'form' => $form->createView(),
154
        ];
155
    }
156
157
    /**
158
     * @Route("/lost-password/reset")
159
     * @Template
160
     * @param Request $request
161
     * @return array|RedirectResponse
162
     */
163
    public function lostPasswordResetAction(Request $request)
164
    {
165
        $redirectToRoute = 'zikulausersmodule_account_menu';
166
167
        if ($this->get('zikula_users_module.current_user')->isLoggedIn()) {
168
            return $this->redirectToRoute($redirectToRoute);
169
        }
170
171
        if (!$request->query->has('id')) {
172
            $this->addFlash('error', $this->__('Your request could not be processed due to missing arguments.'));
173
174
            return $this->redirectToRoute($redirectToRoute);
175
        }
176
177
        $lostPasswordVerificationHelper = $this->get('zikula_zauth_module.helper.lost_password_verification_helper');
178
179
        try {
180
            $requestDetails = $lostPasswordVerificationHelper->decodeLostPasswordId($request->query->get('id'));
181
        } catch (\Exception $e) {
182
            $this->addFlash('error', $this->__('Your request could not be processed.') . ' ' . $e->getMessage());
183
184
            return $this->redirectToRoute($redirectToRoute);
185
        }
186
187
        if ($requestDetails['userId'] == '' || $requestDetails['userName'] == '' || $requestDetails['emailAddress'] == '') {
188
            $this->addFlash('error', $this->__('Your request could not be processed due to invalid arguments.'));
189
190
            return $this->redirectToRoute($redirectToRoute);
191
        }
192
193
        /** @var UserEntity $user */
194
        $user = $this->get('zikula_users_module.user_repository')->find($requestDetails['userId']);
195
        if (null === $user) {
196
            $this->addFlash('error', $this->__('User not found. Please contact the system administrator for assistance.'));
197
198
            return $this->redirectToRoute($redirectToRoute);
199
        }
200
201
        if (!$lostPasswordVerificationHelper->checkConfirmationCode($user->getUid(), $requestDetails['confirmationCode'])) {
202
            $this->addFlash('error', $this->__('Your request could not be processed due to invalid arguments. Maybe your link is expired?'));
203
204
            return $this->redirectToRoute($redirectToRoute);
205
        }
206
207
        $form = $this->createForm(LostPasswordType::class, [], [
208
            'translator' => $this->get('translator.default'),
209
            'includeReset' => true,
210
        ]);
211
        $form->handleRequest($request);
212
        if ($form->isSubmitted() && $form->isValid()) {
213
            $data = $form->getData();
214
            // use authentication method to create zauth mapping if not already created
215
            $authenticationMethods = $this->get('zikula_users_module.internal.authentication_method_collector')->getActive();
216
            $authenticationMethod = array_shift($authenticationMethods);
217
            if (null == $authenticationMethod) {
218
                throw new RuntimeException($this->__('There is no authentication method activated.'));
219
            }
220
            $authenticationMethod->authenticate([
221
                'uname' => $user->getUname(),
222
                'email' => $user->getEmail(),
223
                'pass' => '1234567890'
224
            ]);
225
            // will not authenticate with pass. clear the flashbag of errors.
226
            $this->container->get('session')->getFlashBag()->clear();
227
            // update password
228
            $mappingRepository = $this->get('zikula_zauth_module.authentication_mapping_repository');
229
            $mapping = $mappingRepository->getByZikulaId($user->getUid());
230
            $mapping->setPass($this->get('zikula_zauth_module.api.password')->getHashedPassword($data['pass']));
231
            $mappingRepository->persistAndFlush($mapping);
232
            $this->get('zikula_users_module.helper.access_helper')->login($user);
233
            $this->addFlash('success', $this->__('Your change has been successfully saved. You are now logged in with your new password.'));
234
235
            return $this->redirectToRoute($redirectToRoute);
236
        }
237
238
        return [
239
            'form' => $form->createView()
240
        ];
241
    }
242
243
    /**
244
     * @Route("/change-email")
245
     * @Template
246
     * @param Request $request
247
     * @return array
248
     */
249
    public function changeEmailAction(Request $request)
250
    {
251
        if (!$this->get('zikula_users_module.current_user')->isLoggedIn()) {
252
            throw new AccessDeniedException();
253
        }
254
255
        $form = $this->createForm(ChangeEmailType::class, [], [
256
            'translator' => $this->get('translator.default'),
257
        ]);
258
        $form->handleRequest($request);
259
        if ($form->isSubmitted() && $form->isValid()) {
260
            $data = $form->getData();
261
            $currentUser = $this->get('zikula_users_module.current_user');
262
            $passwordApi = $this->get('zikula_zauth_module.api.password');
263
            $code = $passwordApi->generatePassword();
264
            $this->get('zikula_zauth_module.user_verification_repository')->setVerificationCode($currentUser->get('uid'), ZAuthConstant::VERIFYCHGTYPE_EMAIL, $passwordApi->getHashedPassword($code), $data['email']);
265
            $templateArgs = [
266
                'uname'    => $currentUser->get('uname'),
267
                'email'    => $currentUser->get('email'),
268
                'newemail' => $data['email'],
269
                'url'      => $this->get('router')->generate('zikulazauthmodule_account_confirmchangedemail', ['code' => $code], RouterInterface::ABSOLUTE_URL),
270
            ];
271
            $sent = $this->get('zikula_zauth_module.helper.mail_helper')->sendNotification($data['email'], 'userverifyemail', $templateArgs);
272
            if ($sent) {
273
                $this->addFlash('success', $this->__('Done! You will receive an e-mail to your new e-mail address to confirm the change. You must follow the instructions in that message in order to verify your new address.'));
274
            } else {
275
                $this->addFlash('error', $this->__('Error! There was a problem saving your new e-mail address or sending you a verification message.'));
276
            }
277
278
            return $this->redirectToRoute('zikulausersmodule_account_menu');
279
        }
280
281
        return [
282
            'form' => $form->createView(),
283
        ];
284
    }
285
286
    /**
287
     * @Route("/change-email-confirm/{code}")
288
     * @param null $code
289
     * @return Response
290
     */
291
    public function confirmChangedEmailAction($code = null)
292
    {
293
        if (!$this->get('zikula_users_module.current_user')->isLoggedIn()) {
294
            throw new AccessDeniedException();
295
        }
296
        if (empty($code)) {
297
            return $this->redirectToRoute('zikulausersmodule_account_menu');
298
        }
299
        $emailExpireDays = $this->getVar(ZAuthConstant::MODVAR_EXPIRE_DAYS_CHANGE_EMAIL, ZAuthConstant::DEFAULT_EXPIRE_DAYS_CHANGE_EMAIL);
300
        $this->get('zikula_zauth_module.user_verification_repository')->purgeExpiredRecords($emailExpireDays, ZAuthConstant::VERIFYCHGTYPE_PWD, false);
301
        $currentUser = $this->get('zikula_users_module.current_user');
302
        /** @var UserVerificationEntity $verificationRecord */
303
        $verificationRecord = $this->get('zikula_zauth_module.user_verification_repository')->findOneBy([
304
            'uid' => $currentUser->get('uid'),
305
            'changetype' => ZAuthConstant::VERIFYCHGTYPE_EMAIL
306
        ]);
307
        $validCode = $this->get('zikula_zauth_module.api.password')->passwordsMatch($code, $verificationRecord->getVerifycode());
308
        if (!$validCode) {
309
            $this->addFlash('error', $this->__f('Error! Your e-mail has not been found. After your request you have %s days to confirm the new e-mail address.', ['%s' => $emailExpireDays]));
310
        } else {
311
            $user = $this->get('zikula_users_module.user_repository')->find($currentUser->get('uid'));
312
            $user->setEmail($verificationRecord->getNewemail());
313
            $this->get('zikula_users_module.user_repository')->persistAndFlush($user);
314
            $this->get('zikula_zauth_module.user_verification_repository')->resetVerifyChgFor($user->getUid(), [ZAuthConstant::VERIFYCHGTYPE_EMAIL]);
315
            $this->addFlash('success', $this->__('Done! Changed your e-mail address.'));
316
        }
317
318
        return $this->redirectToRoute('zikulausersmodule_account_menu');
319
    }
320
321
    /**
322
     * @Route("/change-password")
323
     * @Template
324
     * @param Request $request
325
     * @return array
326
     * @throws FatalErrorException|\InvalidArgumentException Thrown if there are no arguments provided or
327
     *                                    if the user is logged in but the user is coming from the login process or
328
     *                                    if the authentication information is invalid
329
     * @throws AccessDeniedException Thrown if the user isn't logged in and isn't coming from the login process
330
     */
331
    public function changePasswordAction(Request $request)
332
    {
333
        // Retrieve and delete any session variables being sent in before we give the function a chance to
334
        // throw an exception. We need to make sure no sensitive data is left dangling in the session variables.
335
        $uid = $request->getSession()->get(UsersConstant::FORCE_PASSWORD_SESSION_UID_KEY);
336
        $authenticationMethod = $request->getSession()->get('authenticationMethod');
337
        $request->getSession()->remove(UsersConstant::FORCE_PASSWORD_SESSION_UID_KEY);
338
        $currentUser = $this->get('zikula_users_module.current_user');
339
340
        // In order to change one's password, the user either must be logged in already, or specifically
341
        // must be coming from the login process. This is an exclusive-or. It is an error if neither is set,
342
        // and likewise if both are set. One or the other, please!
343
        if (!isset($uid) && !$currentUser->isLoggedIn()) {
344
            throw new AccessDeniedException();
345
        } elseif (isset($uid) && $currentUser->isLoggedIn()) {
346
            throw new FatalErrorException();
347
        }
348
349
        if (isset($uid)) {
350
            $login = true;
351
        } else {
352
            $login = false;
353
            $uid = $currentUser->get('uid');
354
        }
355
        $mapping = $this->get('zikula_zauth_module.authentication_mapping_repository')->findOneBy(['uid' => $uid]);
356
357
        $form = $this->createForm(ChangePasswordType::class, [
358
                'uid' => $uid,
359
                'authenticationMethod' => $authenticationMethod
360
            ], [
361
                'translator' => $this->get('translator.default')
362
            ]
363
        );
364
        $form->handleRequest($request);
365
        if ($form->isSubmitted() && $form->isValid()) {
366
            $data = $form->getData();
367
            $mapping->setPass($this->get('zikula_zauth_module.api.password')->getHashedPassword($data['pass']));
368
            $userEntity = $this->get('zikula_users_module.user_repository')->find($mapping->getUid());
369
            $userEntity->delAttribute(ZAuthConstant::REQUIRE_PASSWORD_CHANGE_KEY);
370
            $this->get('zikula_zauth_module.authentication_mapping_repository')->persistAndFlush($mapping);
371
            $this->addFlash('success', $this->__('Password successfully changed.'));
372
            if ($login) {
373
                $this->get('zikula_users_module.helper.access_helper')->login($userEntity);
374
            }
375
376
            return $this->redirectToRoute('zikulausersmodule_account_menu');
377
        }
378
379
        return [
380
            'login' => $login,
381
            'form' => $form->createView(),
382
            'user' => $mapping,
383
            'modvars' => $this->getVars()
384
        ];
385
    }
386
}
387