Failed Conditions
Push — experimental/3.1 ( 3d2ede...2919b9 )
by Yangsin
28:59
created

ForgotController   C

Complexity

Total Complexity 10

Size/Duplication

Total Lines 208
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 19

Test Coverage

Coverage 93.75%

Importance

Changes 0
Metric Value
dl 0
loc 208
ccs 60
cts 64
cp 0.9375
rs 6.875
c 0
b 0
f 0
wmc 10
lcom 1
cbo 19

3 Methods

Rating   Name   Duplication   Size   Complexity  
B index() 0 63 4
A complete() 0 4 1
B reset() 0 63 5
1
<?php
2
/*
3
 * This file is part of EC-CUBE
4
 *
5
 * Copyright(c) 2000-2015 LOCKON CO.,LTD. All Rights Reserved.
6
 *
7
 * http://www.lockon.co.jp/
8
 *
9
 * This program is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU General Public License
11
 * as published by the Free Software Foundation; either version 2
12
 * of the License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
 */
23
24
namespace Eccube\Controller;
25
26
use Doctrine\ORM\EntityManager;
27
use Eccube\Annotation\Component;
28
use Eccube\Annotation\Inject;
29
use Eccube\Application;
30
use Eccube\Event\EccubeEvents;
31
use Eccube\Event\EventArgs;
32
use Eccube\Form\Type\Front\ForgotType;
33
use Eccube\Repository\CustomerRepository;
34
use Eccube\Service\MailService;
35
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
36
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
37
use Symfony\Bridge\Monolog\Logger;
38
use Symfony\Component\EventDispatcher\EventDispatcher;
39
use Symfony\Component\Form\FormFactory;
40
use Symfony\Component\HttpFoundation\Request;
41
use Symfony\Component\HttpKernel\Exception as HttpException;
42
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
43
use Symfony\Component\Validator\Constraints as Assert;
44
use Symfony\Component\Validator\Validator\RecursiveValidator;
45
46
/**
47
 * @Component
48
 * @Route(service=ForgotController::class)
49
 */
50
class ForgotController extends AbstractController
51
{
52
    /**
53
     * @Inject("validator")
54
     * @var RecursiveValidator
55
     */
56
    protected $recursiveValidator;
57
58
    /**
59
     * @Inject("monolog")
60
     * @var Logger
61
     */
62
    protected $logger;
63
64
    /**
65
     * @Inject(MailService::class)
66
     * @var MailService
67
     */
68
    protected $mailService;
69
70
    /**
71
     * @Inject("orm.em")
72
     * @var EntityManager
73
     */
74
    protected $entityManager;
75
76
    /**
77
     * @Inject("config")
78
     * @var array
79
     */
80
    protected $appConfig;
81
82
    /**
83
     * @Inject(CustomerRepository::class)
84
     * @var CustomerRepository
85
     */
86
    protected $customerRepository;
87
88
    /**
89
     * @Inject("eccube.event.dispatcher")
90
     * @var EventDispatcher
91
     */
92
    protected $eventDispatcher;
93
94
    /**
95
     * @Inject("form.factory")
96
     * @var FormFactory
97
     */
98
    protected $formFactory;
99
100
    /**
101
     * @Inject("security.encoder_factory")
102
     * @var EncoderFactoryInterface
103
     */
104
    protected $encoderFactory;
105
106
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$app" missing
Loading history...
introduced by
Doc comment for parameter "$request" missing
Loading history...
107
     * パスワードリマインダ.
108
     *
109
     * @Route("/forgot", name="forgot")
110
     * @Template("Forgot/index.twig")
111
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
112 2
    public function index(Application $app, Request $request)
113
    {
114 2
        $builder = $this->formFactory
115 2
            ->createNamedBuilder('', ForgotType::class);
116
117 2
        $event = new EventArgs(
118
            array(
119 2
                'builder' => $builder,
120
            ),
121 2
            $request
122
        );
123 2
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_FORGOT_INDEX_INITIALIZE, $event);
124
125 2
        $form = $builder->getForm();
126 2
        $form->handleRequest($request);
127
128 2
        if ($form->isSubmitted() && $form->isValid()) {
129 1
            $Customer = $this->customerRepository
130 1
                ->getRegularCustomerByEmail($form->get('login_email')->getData());
131
132 1
            if (!is_null($Customer)) {
133
                // リセットキーの発行・有効期限の設定
134
                $Customer
135 1
                    ->setResetKey($this->customerRepository->getUniqueResetKey())
136 1
                    ->setResetExpire(new \DateTime('+'.$this->appConfig['customer_reset_expire'].' min'));
137
138
                // リセットキーを更新
139 1
                $this->entityManager->persist($Customer);
140 1
                $this->entityManager->flush();
141
142 1
                $event = new EventArgs(
143
                    array(
144 1
                        'form' => $form,
145 1
                        'Customer' => $Customer,
146
                    ),
147 1
                    $request
148
                );
149 1
                $this->eventDispatcher->dispatch(EccubeEvents::FRONT_FORGOT_INDEX_COMPLETE, $event);
150
151
                // 完了URLの生成
152 1
                $reset_url = $app->url('forgot_reset', array('reset_key' => $Customer->getResetKey()));
153
154
                // メール送信
155 1
                $this->mailService->sendPasswordResetNotificationMail($Customer, $reset_url);
156
157
                // ログ出力
158 1
                $this->logger->addInfo(
159 1
                    'send reset password mail to:'."{$Customer->getId()} {$Customer->getEmail()} {$request->getClientIp()}"
0 ignored issues
show
Coding Style introduced by
Variable "Customer" is not in valid camel caps format
Loading history...
160
                );
161
            } else {
162
                log_warning(
163
                    'Un active customer try send reset password email: ',
164
                    array('Enter email' => $form->get('login_email')->getData())
165
                );
166
            }
167
168 1
            return $app->redirect($app->url('forgot_complete'));
169
        }
170
171
        return [
172 1
            'form' => $form->createView(),
173
        ];
174
    }
175
176
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$app" missing
Loading history...
introduced by
Doc comment for parameter "$request" missing
Loading history...
177
     * パスワードリマインダ完了画面.
178
     *
179
     * @Route("/forgot/complete", name="forgot_complete")
180
     * @Template("Forgot/complete.twig")
181
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
182 1
    public function complete(Application $app, Request $request)
0 ignored issues
show
Unused Code introduced by
The parameter $app is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
183
    {
184 1
        return [];
185
    }
186
187
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$app" missing
Loading history...
introduced by
Doc comment for parameter "$request" missing
Loading history...
introduced by
Doc comment for parameter "$reset_key" missing
Loading history...
188
     * パスワード再発行実行画面.
189
     *
190
     * @Route("/forgot/reset/{reset_key}", name="forgot_reset")
191
     * @Template("Forgot/reset.twig")
192
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
193 3
    public function reset(Application $app, Request $request, $reset_key)
0 ignored issues
show
Unused Code introduced by
The parameter $app is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
194
    {
195 3
        $errors = $this->recursiveValidator->validate(
196 3
            $reset_key,
197
            array(
198 3
                new Assert\NotBlank(),
199 3
                new Assert\Regex(
200
                    array(
201 3
                        'pattern' => '/^[a-zA-Z0-9]+$/',
202
                    )
203
                ),
204
            )
205
        );
206
207 3
        if ('GET' === $request->getMethod()
208 3
            && count($errors) === 0
209
        ) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
210
211 2
            $Customer = $this->customerRepository
212 2
                ->getRegularCustomerByResetKey($reset_key);
213 2
            if (is_null($Customer)) {
214 1
                throw new HttpException\NotFoundHttpException('有効期限が切れているか、無効なURLです。');
215
            }
216
217
            // パスワードの発行・更新
218 1
            $encoder = $this->encoderFactory->getEncoder($Customer);
219 1
            $pass = $this->customerRepository->getResetPassword();
220 1
            $Customer->setPassword($pass);
221
222
            // 発行したパスワードの暗号化
223 1
            if ($Customer->getSalt() === null) {
224
                $Customer->setSalt($this->encoderFactory->getEncoder($Customer)->createSalt());
0 ignored issues
show
Bug introduced by
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...
225
            }
226 1
            $encPass = $encoder->encodePassword($pass, $Customer->getSalt());
227 1
            $Customer->setPassword($encPass);
228
229 1
            $Customer->setResetKey(null);
230
231
            // パスワードを更新
232 1
            $this->entityManager->persist($Customer);
233 1
            $this->entityManager->flush();
234
235 1
            $event = new EventArgs(
236
                array(
237 1
                    'Customer' => $Customer,
238
                ),
239 1
                $request
240
            );
241 1
            $this->eventDispatcher->dispatch(EccubeEvents::FRONT_FORGOT_RESET_COMPLETE, $event);
242
243
            // メール送信
244 1
            $this->mailService->sendPasswordResetCompleteMail($Customer, $pass);
245
246
            // ログ出力
247 1
            $this->logger->addInfo(
248 1
                'reset password complete:'."{$Customer->getId()} {$Customer->getEmail()} {$request->getClientIp()}"
0 ignored issues
show
Coding Style introduced by
Variable "Customer" is not in valid camel caps format
Loading history...
249
            );
250
        } else {
251 1
            throw new HttpException\AccessDeniedHttpException('不正なアクセスです。');
252
        }
253
254 1
        return [];
255
    }
256
257
}
258