Completed
Push — 4.0 ( 87d096...bcc1be )
by Kiyotaka
05:44 queued 11s
created

src/Eccube/Controller/ForgotController.php (1 issue)

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
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.ec-cube.co.jp/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Eccube\Controller;
15
16
use Eccube\Event\EccubeEvents;
17
use Eccube\Event\EventArgs;
18
use Eccube\Form\Type\Front\ForgotType;
19
use Eccube\Form\Type\Front\PasswordResetType;
20
use Eccube\Repository\CustomerRepository;
21
use Eccube\Service\MailService;
22
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
23
use Symfony\Component\HttpFoundation\Request;
24
use Symfony\Component\HttpKernel\Exception as HttpException;
25
use Symfony\Component\Routing\Annotation\Route;
26
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
27
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
28
use Symfony\Component\Validator\Constraints as Assert;
29
use Symfony\Component\Validator\Validator\ValidatorInterface;
30
31
class ForgotController extends AbstractController
32
{
33
    /**
34
     * @var ValidatorInterface
35
     */
36
    protected $validator;
37
38
    /**
39
     * @var MailService
40
     */
41
    protected $mailService;
42
43
    /**
44
     * @var CustomerRepository
45
     */
46
    protected $customerRepository;
47
48
    /**
49
     * @var EncoderFactoryInterface
50
     */
51
    protected $encoderFactory;
52
53
    /**
54
     * ForgotController constructor.
55
     *
56
     * @param ValidatorInterface $validator
57
     * @param MailService $mailService
58
     * @param CustomerRepository $customerRepository
59
     * @param EncoderFactoryInterface $encoderFactory
60 4
     */
61
    public function __construct(
62
        ValidatorInterface $validator,
63
        MailService $mailService,
64
        CustomerRepository $customerRepository,
65
        EncoderFactoryInterface $encoderFactory
66 4
    ) {
67 4
        $this->validator = $validator;
68 4
        $this->mailService = $mailService;
69 4
        $this->customerRepository = $customerRepository;
70
        $this->encoderFactory = $encoderFactory;
71
    }
72
73
    /**
74
     * パスワードリマインダ.
75
     *
76
     * @Route("/forgot", name="forgot")
77
     * @Template("Forgot/index.twig")
78 1
     */
79
    public function index(Request $request)
80 1
    {
81
        if ($this->isGranted('ROLE_USER')) {
82
            throw new HttpException\NotFoundHttpException();
83
        }
84 1
85 1
        $builder = $this->formFactory
86
            ->createNamedBuilder('', ForgotType::class);
87 1
88
        $event = new EventArgs(
89 1
            [
90
                'builder' => $builder,
91 1
            ],
92
            $request
93 1
        );
94
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_FORGOT_INDEX_INITIALIZE, $event);
95 1
96 1
        $form = $builder->getForm();
97
        $form->handleRequest($request);
98 1
99
        if ($form->isSubmitted() && $form->isValid()) {
100
            $Customer = $this->customerRepository
101
                ->getRegularCustomerByEmail($form->get('login_email')->getData());
102
103
            if (!is_null($Customer)) {
104
                // リセットキーの発行・有効期限の設定
105
                $Customer
106
                    ->setResetKey($this->customerRepository->getUniqueResetKey())
107
                    ->setResetExpire(new \DateTime('+'.$this->eccubeConfig['eccube_customer_reset_expire'].' min'));
108
109
                // リセットキーを更新
110
                $this->entityManager->persist($Customer);
111
                $this->entityManager->flush();
112
113
                $event = new EventArgs(
114
                    [
115
                        'form' => $form,
116
                        'Customer' => $Customer,
117
                    ],
118
                    $request
119
                );
120
                $this->eventDispatcher->dispatch(EccubeEvents::FRONT_FORGOT_INDEX_COMPLETE, $event);
121
122
                // 完了URLの生成
123
                $reset_url = $this->generateUrl('forgot_reset', ['reset_key' => $Customer->getResetKey()], UrlGeneratorInterface::ABSOLUTE_URL);
124
125
                // メール送信
126
                $this->mailService->sendPasswordResetNotificationMail($Customer, $reset_url);
127
128
                // ログ出力
129
                log_info('send reset password mail to:'."{$Customer->getId()} {$Customer->getEmail()} {$request->getClientIp()}");
130
            } else {
131
                log_warning(
132
                    'Un active customer try send reset password email: ',
133
                    ['Enter email' => $form->get('login_email')->getData()]
134
                );
135
            }
136
137
            return $this->redirectToRoute('forgot_complete');
138
        }
139
140 1
        return [
141
            'form' => $form->createView(),
142
        ];
143
    }
144
145
    /**
146
     * 再設定URL送信完了画面.
147
     *
148
     * @Route("/forgot/complete", name="forgot_complete")
149
     * @Template("Forgot/complete.twig")
150 1
     */
151
    public function complete(Request $request)
152 1
    {
153
        if ($this->isGranted('ROLE_USER')) {
154
            throw new HttpException\NotFoundHttpException();
155
        }
156 1
157
        return [];
158
    }
159
160
    /**
161
     * パスワード再発行実行画面.
162
     *
163
     * @Route("/forgot/reset/{reset_key}", name="forgot_reset")
164
     * @Template("Forgot/reset.twig")
165 2
     */
166
    public function reset(Request $request, $reset_key)
167 2
    {
168
        if ($this->isGranted('ROLE_USER')) {
169
            throw new HttpException\NotFoundHttpException();
170
        }
171 2
172 2
        $errors = $this->validator->validate(
173
            $reset_key,
174 2
            [
175 2
                new Assert\NotBlank(),
176
                new Assert\Regex(
177 2
                    [
178
                        'pattern' => '/^[a-zA-Z0-9]+$/',
179
                    ]
180
                ),
181
            ]
182
        );
183 2
184 2
        if (count($errors) > 0) {
185
            // リセットキーに異常がある場合
186 1
            throw new HttpException\NotFoundHttpException();
187 1
        }
188 1
189 1
        $Customer = $this->customerRepository
190
            ->getRegularCustomerByResetKey($reset_key);
191
192
        if (null === $Customer) {
193
            // リセットキーから会員データが取得できない場合
194
            throw new HttpException\NotFoundHttpException();
195
        }
196
197
        $builder = $this->formFactory
198
            ->createNamedBuilder('', PasswordResetType::class);
199
200
        $form = $builder->getForm();
201
        $form->handleRequest($request);
202
        $error = null;
203
204
        if ($form->isSubmitted() && $form->isValid()) {
205
            // リセットキー・入力メールアドレスで会員情報検索
206
            $Customer = $this->customerRepository
207
                ->getRegularCustomerByResetKey($reset_key, $form->get('login_email')->getData());
208
            if ($Customer) {
209
                // パスワードの発行・更新
210
                $encoder = $this->encoderFactory->getEncoder($Customer);
211
                $pass = $form->get('password')->getData();
212
                $Customer->setPassword($pass);
213
214
                // 発行したパスワードの暗号化
215
                if ($Customer->getSalt() === null) {
216
                    $Customer->setSalt($this->encoderFactory->getEncoder($Customer)->createSalt());
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Symfony\Component\Securi...asswordEncoderInterface as the method createSalt() does only exist in the following implementations of said interface: Eccube\Security\Core\Encoder\PasswordEncoder.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
217
                }
218
                $encPass = $encoder->encodePassword($pass, $Customer->getSalt());
219
220
                // パスワードを更新
221
                $Customer->setPassword($encPass);
222
                // リセットキーをクリア
223 1
                $Customer->setResetKey(null);
224
225
                // パスワードを更新
226
                $this->entityManager->persist($Customer);
227
                $this->entityManager->flush();
228
229
                $event = new EventArgs(
230
                    [
231
                        'Customer' => $Customer,
232
                    ],
233
                    $request
234
                );
235
                $this->eventDispatcher->dispatch(EccubeEvents::FRONT_FORGOT_RESET_COMPLETE, $event);
236
237
                // 完了メッセージを設定
238
                $this->addFlash('password_reset_complete', trans('front.forgot.reset_complete'));
239
240
                // ログインページへリダイレクト
241
                return $this->redirectToRoute('mypage_login');
242
            } else {
243
                // リセットキー・メールアドレスから会員データが取得できない場合
244
                $error = trans('front.forgot.reset_not_found');
245
            }
246
        }
247
248
        return [
249
            'error' => $error,
250
            'form' => $form->createView(),
251
        ];
252
    }
253
}
254