From 5b1ba71a8a6eacbe1e03d9a92b53c5761bb5231b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 16 Aug 2018 13:41:32 +0200 Subject: [PATCH] require actual password for change + insert link in menu --- Controller/PasswordController.php | 66 ++++++++++++++++----- Form/UserPasswordType.php | 59 +++++++++++++++--- Resources/config/routing.yml | 4 +- Resources/config/services/controller.yml | 6 ++ Resources/config/services/form.yml | 7 +++ Resources/config/services/menu.yml | 2 + Resources/translations/messages.fr.yml | 6 +- Resources/views/Password/password.html.twig | 20 +++++-- Routing/MenuBuilder/UserMenuBuilder.php | 35 +++++++++-- 9 files changed, 169 insertions(+), 36 deletions(-) diff --git a/Controller/PasswordController.php b/Controller/PasswordController.php index ae85b83a1..f6ea24bea 100644 --- a/Controller/PasswordController.php +++ b/Controller/PasswordController.php @@ -5,12 +5,42 @@ namespace Chill\MainBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Form\Extension\Core\Type\SubmitType; - use Chill\MainBundle\Form\UserPasswordType; use Chill\MainBundle\Entity\User; +use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; +use Symfony\Component\Translation\TranslatorInterface; +use Psr\Log\LoggerInterface; class PasswordController extends Controller { + /** + * + * @var UserPasswordEncoderInterface + */ + protected $passwordEncoder; + + /** + * + * @var TranslatorInterface + */ + protected $translator; + + /** + * + * @var LoggerInterface + */ + protected $chillLogger; + + public function __construct( + LoggerInterface $chillLogger, + UserPasswordEncoderInterface $passwordEncoder, + TranslatorInterface $translator + ) { + $this->chillLogger = $chillLogger; + $this->passwordEncoder = $passwordEncoder; + $this->translator = $translator; + } + /** * * @param Request $request @@ -18,7 +48,6 @@ class PasswordController extends Controller */ public function UserPasswordAction(Request $request) { - // get authentified user $user = $this->getUser(); @@ -29,21 +58,27 @@ class PasswordController extends Controller $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - - - $password = $form->getData()->getPassword(); + $password = $form->get('new_password')->getData(); // logging for prod - $this->get('logger')->info('update password for an user', - array('method' => __METHOD__, 'user' => $user->getUsername())); + $this + ->chillLogger + ->notice( + 'update password for an user', + array( + 'method' => $request->getMethod(), + 'user' => $user->getUsername() + ) + ); - $user->setPassword($this->get('security.password_encoder') - ->encodePassword($user, $password)); + $user->setPassword($this->passwordEncoder->encodePassword($user, $password)); $em = $this->getDoctrine()->getManager(); $em->flush(); - $this->addFlash('success', $this->get('translator')->trans('Password successfully updated!')); + $this->addFlash('success', $this->translator->trans('Password successfully updated!')); + + return $this->redirectToRoute('change_my_password'); } @@ -62,11 +97,12 @@ class PasswordController extends Controller */ private function passwordForm(User $user) { - return $this->createForm(UserPasswordType::class, $user, array( - 'method' => 'PUT', - - )) - ->add('submit', SubmitType::class, array('label' => 'Change password')) + return $this->createForm( + UserPasswordType::class, + [], + [ 'user' => $this->getUser() ] + ) + ->add('submit', SubmitType::class, array('label' => 'Change password')) ; } diff --git a/Form/UserPasswordType.php b/Form/UserPasswordType.php index 5e00dd707..4486b5643 100644 --- a/Form/UserPasswordType.php +++ b/Form/UserPasswordType.php @@ -10,9 +10,34 @@ use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\Regex; use Symfony\Component\Form\Extension\Core\Type\RepeatedType; use Symfony\Component\Form\Extension\Core\Type\PasswordType; +use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; +use Symfony\Component\Validator\Context\ExecutionContextInterface; +use Symfony\Component\Validator\Constraints\Callback; +use Psr\Log\LoggerInterface; class UserPasswordType extends AbstractType { + /** + * + * @var UserPasswordEncoderInterface + */ + protected $passwordEncoder; + + /** + * + * @var LoggerInterface + */ + protected $chillLogger; + + public function __construct( + UserPasswordEncoderInterface $passwordEncoder, + LoggerInterface $chillLogger + ) { + $this->passwordEncoder = $passwordEncoder; + $this->chillLogger = $chillLogger; + } + + /** * @param FormBuilderInterface $builder * @param array $options @@ -20,7 +45,7 @@ class UserPasswordType extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('password', RepeatedType::class, array( + ->add('new_password', RepeatedType::class, array( 'type' => PasswordType::class, 'required' => false, 'options' => array(), @@ -45,17 +70,35 @@ class UserPasswordType extends AbstractType )) ) )) + ->add('actual_password', PasswordType::class, [ + 'label' => 'Your actual password', + 'mapped' => false, + 'constraints' => [ + new Callback([ + 'callback' => function($password, ExecutionContextInterface $context, $payload) use ($options) { + if (TRUE === $this->passwordEncoder->isPasswordValid($options['user'], $password)) { + return; + } + + // password problem :-) + $this->chillLogger + ->notice("incorrect password when trying to change password", [ + 'username' => $options['user']->getUsername() + ]); + $context->addViolation('Incorrect password'); + } + ]) + ] + ]) ; } - - /** - * @param OptionsResolverInterface $resolver - */ + public function configureOptions(OptionsResolver $resolver) { - $resolver->setDefaults(array( - 'data_classds' => 'Chill\MainBundle\Entity\User' - )); + $resolver + ->setRequired('user') + ->setAllowedTypes('user', \Chill\MainBundle\Entity\User::class) + ; } /** diff --git a/Resources/config/routing.yml b/Resources/config/routing.yml index 5a9414fdd..8d1c5fe01 100644 --- a/Resources/config/routing.yml +++ b/Resources/config/routing.yml @@ -79,6 +79,6 @@ login_check: logout: path: /logout -password: - path: /password +change_my_password: + path: /{_locale}/password/edit defaults: { _controller: ChillMainBundle:Password:userPassword } \ No newline at end of file diff --git a/Resources/config/services/controller.yml b/Resources/config/services/controller.yml index 7ead910ec..562753837 100644 --- a/Resources/config/services/controller.yml +++ b/Resources/config/services/controller.yml @@ -4,3 +4,9 @@ services: autowire: true resource: '../../../Controller' tags: ['controller.service_arguments'] + + Chill\MainBundle\Controller\PasswordController: + autowire: true + arguments: + $chillLogger: '@monolog.logger.chill' + tags: ['controller.service_arguments'] diff --git a/Resources/config/services/form.yml b/Resources/config/services/form.yml index 78bfa0280..293de5e7c 100644 --- a/Resources/config/services/form.yml +++ b/Resources/config/services/form.yml @@ -123,5 +123,12 @@ services: class: Chill\MainBundle\Form\AdvancedSearchType arguments: - "@chill.main.search_provider" + tags: + - { name: form.type } + + Chill\MainBundle\Form\UserPasswordType: + arguments: + $chillLogger: '@monolog.logger.chill' + $passwordEncoder: '@Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface' tags: - { name: form.type } \ No newline at end of file diff --git a/Resources/config/services/menu.yml b/Resources/config/services/menu.yml index 0de492e3e..b56c34d4e 100644 --- a/Resources/config/services/menu.yml +++ b/Resources/config/services/menu.yml @@ -1,5 +1,7 @@ services: Chill\MainBundle\Routing\MenuBuilder\UserMenuBuilder: + arguments: + $tokenStorage: '@Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface' tags: - { name: 'chill.menu_builder' } diff --git a/Resources/translations/messages.fr.yml b/Resources/translations/messages.fr.yml index 24c4eb710..1d7fc866c 100644 --- a/Resources/translations/messages.fr.yml +++ b/Resources/translations/messages.fr.yml @@ -187,4 +187,8 @@ Choose the format: Choisir le format # select2 'select2.no_results': Aucun résultat 'select2.error_loading': Erreur de chargement des résultats -'select2.searching': Recherche en cours... \ No newline at end of file +'select2.searching': Recherche en cours... + +# page changement mot de passe +Change my password: Modification du mot de passe +Your actual password: Mot de passe actuel \ No newline at end of file diff --git a/Resources/views/Password/password.html.twig b/Resources/views/Password/password.html.twig index d31d43026..de2a18fbd 100644 --- a/Resources/views/Password/password.html.twig +++ b/Resources/views/Password/password.html.twig @@ -23,12 +23,22 @@ {% block title %}{{"Change my password"|trans}}{% endblock %} {% block content %} +
-

{{ 'Choose a new password'|trans }}

+

{{ 'Change my password'|trans }}

- {{ form_start(form) }} - {{ form_row(form.password) }} - {{ form_widget(form.submit, { 'attr': { 'class': 'sc-button orange' } } ) }} - {{ form_end(form) }} + {{ form_start(form) }} + {{ form_row(form.actual_password) }} + {{ form_row(form.new_password) }} + + + + {{ form_end(form) }} + +
{% endblock %} diff --git a/Routing/MenuBuilder/UserMenuBuilder.php b/Routing/MenuBuilder/UserMenuBuilder.php index aaa9e82e8..010f5609d 100644 --- a/Routing/MenuBuilder/UserMenuBuilder.php +++ b/Routing/MenuBuilder/UserMenuBuilder.php @@ -18,6 +18,8 @@ namespace Chill\MainBundle\Routing\MenuBuilder; use Chill\MainBundle\Routing\LocalMenuBuilderInterface; +use Chill\MainBundle\Entity\User; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; /** * @@ -26,17 +28,40 @@ use Chill\MainBundle\Routing\LocalMenuBuilderInterface; */ class UserMenuBuilder implements LocalMenuBuilderInterface { + /** + * + * @var TokenStorageInterface + */ + protected $tokenStorage; + + public function __construct(TokenStorageInterface $tokenStorage) + { + $this->tokenStorage = $tokenStorage; + } + public function buildMenu($menuId, \Knp\Menu\MenuItem $menu, array $parameters) { - $menu->addChild( + if ($this->tokenStorage->getToken()->getUser() instanceof User) { + $menu + ->addChild( + 'Change password', + [ 'route' => 'change_my_password'] + ) + ->setExtras([ + 'order' => 99999999998 + ]); + } + + $menu + ->addChild( 'Logout', [ 'route' => 'logout' ]) - ->setExtras([ - 'order'=> 99999999999, - 'icon' => 'power-off' - ]); + ->setExtras([ + 'order'=> 99999999999, + 'icon' => 'power-off' + ]); } public static function getMenuIds(): array