diff --git a/app/bootstrap.php.d/70-security.php b/app/bootstrap.php.d/70-security.php index ad428d4..845aca2 100644 --- a/app/bootstrap.php.d/70-security.php +++ b/app/bootstrap.php.d/70-security.php @@ -6,10 +6,8 @@ use Silex\Provider\RememberMeServiceProvider; use Gist\Service\SaltGenerator; use Gist\Security\AuthenticationProvider; use Gist\Security\AuthenticationListener; -use Gist\Security\AuthenticationEntryPoint; use Gist\Security\LogoutSuccessHandler; use Silex\Provider\SessionServiceProvider; -use Symfony\Component\Security\Http\HttpUtils; $app['enable_registration'] = true; $app['enable_login'] = true; @@ -19,7 +17,7 @@ $app['login_required_to_view_embeded_gist'] = false; $app['token'] = 'ThisTokenIsNotSoSecretChangeIt'; -$app['salt_generator'] = $app->share(function($app) { +$app['salt_generator'] = $app->share(function ($app) { return new SaltGenerator(); }); @@ -34,10 +32,10 @@ $app['security.authentication_listener.factory.form'] = $app->protect(function ( $app['security.authentication_provider.'.$name.'.form'] = $app->share(function ($app) { return new AuthenticationProvider($app['user.provider']); }); - + $app['security.authentication_listener.'.$name.'.form'] = $app->share(function ($app) use ($name) { return new AuthenticationListener( - $app['security.token_storage'], + $app['security.token_storage'], $app['security.authentication_provider.'.$name.'.form'] ); }); @@ -46,7 +44,7 @@ $app['security.authentication_listener.factory.form'] = $app->protect(function ( 'security.authentication_provider.'.$name.'.form', 'security.authentication_listener.'.$name.'.form', null, - 'pre_auth' + 'pre_auth', ]; }); @@ -76,13 +74,12 @@ $firewall = [ ], 'security.access_rules' => [ ['^/[a-z]{2}/my.*$', 'ROLE_USER'], - ] + ], ]; if ($app['login_required_to_edit_gist'] || $app['login_required_to_view_gist'] || $app['login_required_to_view_embeded_gist']) { - $securityRegexp = '^/[a-z]{2}'; $exceptedUriPattern = ['login', 'register']; - + if ($app['login_required_to_view_gist'] === true) { $firewall['security.access_rules'][] = ['^/[a-z]{2}/view.*$', 'ROLE_USER']; $firewall['security.access_rules'][] = ['^/[a-z]{2}/revs.*$', 'ROLE_USER']; @@ -90,7 +87,7 @@ if ($app['login_required_to_edit_gist'] || $app['login_required_to_view_gist'] | $exceptedUriPattern[] = 'view'; $exceptedUriPattern[] = 'revs'; } - + if ($app['login_required_to_view_embeded_gist'] === true) { $firewall['security.access_rules'][] = ['^/[a-z]{2}/embed.*$', 'ROLE_USER']; } else { @@ -98,10 +95,10 @@ if ($app['login_required_to_edit_gist'] || $app['login_required_to_view_gist'] | } if ($app['login_required_to_edit_gist'] === true) { - $firewall['security.access_rules'][] = ['^/[a-z]{2}/(?!('.implode('|', $exceptedUriPattern).')).*$', 'ROLE_USER']; + $firewall['security.access_rules'][] = ['^/[a-z]{2}/(?!('.implode('|', $exceptedUriPattern).')).*$', 'ROLE_USER']; } } - + $app->register(new SecurityServiceProvider(), $firewall); $app->register(new SessionServiceProvider()); $app->register(new RememberMeServiceProvider()); diff --git a/app/locales/de.yml b/app/locales/de.yml index 9655ec0..42cc012 100644 --- a/app/locales/de.yml +++ b/app/locales/de.yml @@ -59,12 +59,15 @@ login: form: username: placeholder: 'Username' + current_password: + placeholder: 'Aktuelles Passwort' password: placeholder: 'Password' form: error: - not_blank: 'This value should not be blank bro!' + password: 'Ungültiges Passwort.' + not_blank: 'Dieser Wert darf nicht leer sein.' title: placeholder: 'Titel' cipher: @@ -75,7 +78,9 @@ form: no: 'No' submit: 'Senden' filter: 'Filter' + confirm: 'Bestätigen Sie?' success: + password: 'Passwort aktualisiert.' gist: 'Gist removed.' type: label: 'Language: %value%' diff --git a/app/locales/en.yml b/app/locales/en.yml index 4c4b8cc..51630d3 100644 --- a/app/locales/en.yml +++ b/app/locales/en.yml @@ -59,11 +59,14 @@ login: form: username: placeholder: 'Username' + current_password: + placeholder: 'Current password' password: placeholder: 'Password' form: error: + password: 'Invalid password.' not_blank: 'This value should not be blank bro!' title: placeholder: 'Title' @@ -75,7 +78,9 @@ form: no: 'No' submit: 'Send' filter: 'Filter' + confirm: 'Do you confirm?' success: + password: 'Password updated.' gist: 'Gist removed.' type: label: 'Language: %value%' diff --git a/app/locales/es.yml b/app/locales/es.yml index bb8fe73..75cfe90 100644 --- a/app/locales/es.yml +++ b/app/locales/es.yml @@ -59,11 +59,14 @@ login: form: username: placeholder: 'Nombre de usuario' + current_password: + placeholder: 'Contraseña actual' password: placeholder: 'Contraseña' form: error: + password: 'Contraseña no válida.' not_blank: 'Se necesita introducir este dato.' title: placeholder: 'Título' @@ -75,7 +78,9 @@ form: no: 'No' submit: 'Enviar' filter: 'Filtrar' + confirm: '¿Confirmas?' success: + password: 'Contraseña cambiada.' gist: 'Su Gist ha sido eliminado.' type: label: 'Lenguaje : %value%' diff --git a/app/locales/fr.yml b/app/locales/fr.yml index 20095b4..7674d1b 100644 --- a/app/locales/fr.yml +++ b/app/locales/fr.yml @@ -59,11 +59,14 @@ login: form: username: placeholder: 'Nom d''utilisateur' + current_password: + placeholder: 'Mot de passe courrant' password: placeholder: 'Mot de passe' form: error: + password: 'Mot de passe invalide.' not_blank: 'Vous devez saisir cette donnée.' title: placeholder: 'Titre' @@ -75,7 +78,9 @@ form: no: 'Non' submit: 'Envoyer' filter: 'Filtrer' + confirm: 'Confirmez-vous ?' success: + password: 'Mot de passe modifié.' gist: 'Votre Gist a bien a été supprimé.' type: label: 'Langage : %value%' diff --git a/src/Gist/Controller/MyController.php b/src/Gist/Controller/MyController.php index d097b6a..aa113c8 100644 --- a/src/Gist/Controller/MyController.php +++ b/src/Gist/Controller/MyController.php @@ -5,6 +5,8 @@ namespace Gist\Controller; use Symfony\Component\HttpFoundation\Request; use Gist\Form\DeleteGistForm; use Gist\Form\FilterGistForm; +use Gist\Form\UserPasswordForm; +use Symfony\Component\HttpFoundation\RedirectResponse; /** * Class MyController. @@ -43,6 +45,9 @@ class MyController extends Controller $filterForm = $filterForm->build()->getForm(); + $passwordForm = new UserPasswordForm($app['form.factory'], $app['translator']); + $passwordForm = $passwordForm->build()->getForm(); + if ($request->query->has('filter')) { $filterForm->submit($request); @@ -54,9 +59,10 @@ class MyController extends Controller $gists = $this->getUser()->getGistsPager($page, $options); if ($request->isMethod('post')) { - $deleteForm->submit($request); + $deleteForm->handleRequest($request); + $passwordForm->handleRequest($request); - if ($deleteForm->isValid()) { + if ($deleteForm->isSubmitted() && $deleteForm->isValid()) { $id = (int) $deleteForm->getData()['id']; foreach ($gists as $gist) { @@ -67,6 +73,30 @@ class MyController extends Controller } } } + + if ($passwordForm->isSubmitted() && $passwordForm->isValid()) { + $currentPassword = $passwordForm->getData()['currentPassword']; + $newPassword = $passwordForm->getData()['newPassword']; + $passwordUpdated = 0; + + if ($app['user.provider']->isCurrentUserPassword($this->getUser(), $currentPassword)) { + $app['user.provider']->updateUserPassword( + $this->getUser(), + $newPassword + ); + + $passwordUpdated = 1; + } + + return new RedirectResponse( + $app['url_generator']->generate( + 'my', + [ + 'passwordUpdated' => $passwordUpdated, + ] + ) + ); + } } return $this->render( @@ -76,6 +106,7 @@ class MyController extends Controller 'page' => $page, 'deleteForm' => $deleteForm->createView(), 'filterForm' => $filterForm->createView(), + 'passwordForm' => $passwordForm->createView(), 'deleted' => !empty($deleted), ) ); diff --git a/src/Gist/Form/UserPasswordForm.php b/src/Gist/Form/UserPasswordForm.php new file mode 100644 index 0000000..9a625b3 --- /dev/null +++ b/src/Gist/Form/UserPasswordForm.php @@ -0,0 +1,65 @@ + + */ +class UserPasswordForm extends AbstractForm +{ + /** + * {@inheritdoc} + */ + public function build(array $options = array()) + { + $this->builder->add( + 'currentPassword', + 'password', + array( + 'required' => true, + 'attr' => array( + 'class' => 'form-control', + 'placeholder' => $this->translator->trans('login.register.form.current_password.placeholder'), + ), + 'trim' => false, + 'constraints' => array( + new NotBlank(array( + 'message' => $this->translator->trans('form.error.not_blank'), + )), + ), + ) + ); + + $this->builder->add( + 'newPassword', + 'password', + array( + 'required' => true, + 'attr' => array( + 'class' => 'form-control', + 'placeholder' => $this->translator->trans('login.register.form.password.placeholder'), + ), + 'trim' => false, + 'constraints' => array( + new NotBlank(array( + 'message' => $this->translator->trans('form.error.not_blank'), + )), + ), + ) + ); + + return $this->builder; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'password'; + } +} diff --git a/src/Gist/Resources/views/My/my.html.twig b/src/Gist/Resources/views/My/my.html.twig index 6045781..bf3f44c 100644 --- a/src/Gist/Resources/views/My/my.html.twig +++ b/src/Gist/Resources/views/My/my.html.twig @@ -4,6 +4,20 @@ {% block body %}
+ {% if app.request.query.has('passwordUpdated') %} +
+ {% if app.request.query.get('passwordUpdated') %} +
+

{{ 'form.success.password'|trans }}

+
+ {% else %} +
+

{{ 'form.error.password'|trans }}

+
+ {% endif %} +
+ {% endif %} + {% if deleted %}
@@ -11,6 +25,7 @@
{% endif %} +
@@ -42,15 +57,15 @@
+ +
+
+ {{ 'login.login.form.password.placeholder'|trans }} +
+
+
+
+

+ {{ form_errors(passwordForm.currentPassword) }} + {{ form_widget(passwordForm.currentPassword) }} +

+ +

+ {{ form_errors(passwordForm.newPassword) }} + {{ form_widget(passwordForm.newPassword) }} +

+ +

+ {{ form_rest(passwordForm) }} + +

+
+
+
+
{% endblock %} diff --git a/src/Gist/Resources/views/base.html.twig b/src/Gist/Resources/views/base.html.twig index fedafa1..ca0418b 100644 --- a/src/Gist/Resources/views/base.html.twig +++ b/src/Gist/Resources/views/base.html.twig @@ -94,6 +94,19 @@ {% endblock %} {% block js %} + diff --git a/src/Gist/Service/UserProvider.php b/src/Gist/Service/UserProvider.php index cc4521d..beab8e1 100644 --- a/src/Gist/Service/UserProvider.php +++ b/src/Gist/Service/UserProvider.php @@ -166,6 +166,19 @@ class UserProvider implements UserProviderInterface return $user; } + /* + * Checks if the given password is the current user password. + * + * @param User $user + * @param string $password + * + * @return bool + */ + public function isCurrentUserPassword(User $user, $password) + { + return $this->encoder->encodePassword($password, $user->getSalt()) === $user->getPassword(); + } + /** * Refresh an user. * diff --git a/web/app/js/app.js b/web/app/js/app.js index 4ce91e0..a4d64d4 100644 --- a/web/app/js/app.js +++ b/web/app/js/app.js @@ -93,8 +93,10 @@ var editorEvents = function() { var myEvents = function() { $('.btn-delete').click(function() { - $('#delete_id').val($(this).data('id')); - $('#form-deletion form').submit(); + if (confirm(trans('form.confirm'))) { + $('#delete_id').val($(this).data('id')); + $('#form-deletion form').submit(); + } }); } @@ -136,7 +138,7 @@ var viewerEvents = function() { $(document).ready(function() { var key = getKey(); - + var $cipherEditor = $('.cipher-editor'); var $embedInput = $('#embed-input');