diff --git a/app/config/services.yml b/app/config/services.yml
index d4485e429..91a03e10e 100644
--- a/app/config/services.yml
+++ b/app/config/services.yml
@@ -3,6 +3,7 @@
parameters:
security.authentication.provider.dao.class: Wallabag\CoreBundle\Security\Authentication\Provider\WallabagAuthenticationProvider
security.encoder.digest.class: Wallabag\CoreBundle\Security\Authentication\Encoder\WallabagPasswordEncoder
+ security.validator.user_password.class: Wallabag\CoreBundle\Security\Validator\WallabagUserPasswordValidator
services:
# service_name:
diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php
index f48a9cb19..7540f756c 100644
--- a/src/Wallabag/CoreBundle/Controller/ConfigController.php
+++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php
@@ -7,6 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Wallabag\CoreBundle\Entity\Config;
use Wallabag\CoreBundle\Form\Type\ConfigType;
+use Wallabag\CoreBundle\Form\Type\ChangePasswordType;
class ConfigController extends Controller
{
@@ -14,19 +15,18 @@ class ConfigController extends Controller
* @param Request $request
*
* @Route("/config", name="config")
- *
- * @return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction(Request $request)
{
+ $em = $this->getDoctrine()->getManager();
$config = $this->getConfig();
- $form = $this->createForm(new ConfigType(), $config);
+ // handle basic config detail
+ $configForm = $this->createForm(new ConfigType(), $config);
+ $configForm->handleRequest($request);
- $form->handleRequest($request);
+ if ($configForm->isValid()) {
- if ($form->isValid()) {
- $em = $this->getDoctrine()->getManager();
$em->persist($config);
$em->flush();
@@ -38,11 +38,36 @@ class ConfigController extends Controller
return $this->redirect($this->generateUrl('config'));
}
+ // handle changing password
+ $pwdForm = $this->createForm(new ChangePasswordType());
+ $pwdForm->handleRequest($request);
+
+ if ($pwdForm->isValid()) {
+ $user = $this->getUser();
+ $user->setPassword($pwdForm->get('new_password')->getData());
+ $em->persist($user);
+ $em->flush();
+
+ $this->get('session')->getFlashBag()->add(
+ 'notice',
+ 'Password updated'
+ );
+
+ return $this->redirect($this->generateUrl('config'));
+ }
+
return $this->render('WallabagCoreBundle:Config:index.html.twig', array(
- 'form' => $form->createView(),
+ 'configForm' => $configForm->createView(),
+ 'pwdForm' => $pwdForm->createView(),
));
}
+ /**
+ * Retrieve config for the current user.
+ * If no config were found, create a new one.
+ *
+ * @return Wallabag\CoreBundle\Entity\Config
+ */
private function getConfig()
{
$config = $this->getDoctrine()
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadUserData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadUserData.php
index e4751f202..d99412f48 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadUserData.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadUserData.php
@@ -18,7 +18,7 @@ class LoadUserData extends AbstractFixture implements OrderedFixtureInterface
$userAdmin->setName('Big boss');
$userAdmin->setEmail('bigboss@wallabag.org');
$userAdmin->setUsername('admin');
- $userAdmin->setPassword('test');
+ $userAdmin->setPassword('mypassword');
$manager->persist($userAdmin);
@@ -28,7 +28,7 @@ class LoadUserData extends AbstractFixture implements OrderedFixtureInterface
$bobUser->setName('Bobby');
$bobUser->setEmail('bobby@wallabag.org');
$bobUser->setUsername('bob');
- $bobUser->setPassword('test');
+ $bobUser->setPassword('mypassword');
$manager->persist($bobUser);
diff --git a/src/Wallabag/CoreBundle/Form/Type/ChangePasswordType.php b/src/Wallabag/CoreBundle/Form/Type/ChangePasswordType.php
new file mode 100644
index 000000000..8bf4ca1ef
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Form/Type/ChangePasswordType.php
@@ -0,0 +1,40 @@
+add('old_password', 'password', array(
+ 'constraints' => new UserPassword(array('message' => 'Wrong value for your current password')),
+ ))
+ ->add('new_password', 'repeated', array(
+ 'type' => 'password',
+ 'invalid_message' => 'The password fields must match.',
+ 'required' => true,
+ 'first_options' => array('label' => 'New password'),
+ 'second_options' => array('label' => 'Repeat new password'),
+ 'constraints' => array(
+ new Constraints\Length(array(
+ 'min' => 6,
+ 'minMessage' => 'Password should by at least 6 chars long'
+ )),
+ new Constraints\NotBlank()
+ )
+ ))
+ ->add('save', 'submit')
+ ;
+ }
+
+ public function getName()
+ {
+ return 'change_passwd';
+ }
+}
diff --git a/src/Wallabag/CoreBundle/Resources/views/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/Config/index.html.twig
index 9c04ff206..941451778 100644
--- a/src/Wallabag/CoreBundle/Resources/views/Config/index.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/Config/index.html.twig
@@ -7,35 +7,67 @@
{% endblock %}
{% block content %}
-
Basic config
+ {% trans %}Basic config{% endtrans %}
-
+
+ {% trans %}Change your password{% endtrans %}
+
+
{% endblock %}
diff --git a/src/Wallabag/CoreBundle/Security/Authentication/Encoder/WallabagPasswordEncoder.php b/src/Wallabag/CoreBundle/Security/Authentication/Encoder/WallabagPasswordEncoder.php
index 56f1affe3..fcfe418bf 100644
--- a/src/Wallabag/CoreBundle/Security/Authentication/Encoder/WallabagPasswordEncoder.php
+++ b/src/Wallabag/CoreBundle/Security/Authentication/Encoder/WallabagPasswordEncoder.php
@@ -41,10 +41,6 @@ class WallabagPasswordEncoder extends BasePasswordEncoder
*/
public function encodePassword($raw, $salt)
{
- if (null === $this->username) {
- throw new \LogicException('We can not check the password without a username.');
- }
-
if ($this->isPasswordTooLong($raw)) {
throw new BadCredentialsException('Invalid password.');
}
@@ -71,6 +67,10 @@ class WallabagPasswordEncoder extends BasePasswordEncoder
*/
protected function mergePasswordAndSalt($password, $salt)
{
+ if (null === $this->username) {
+ throw new \LogicException('We can not check the password without a username.');
+ }
+
if (empty($salt)) {
return $password;
}
diff --git a/src/Wallabag/CoreBundle/Security/Validator/WallabagUserPasswordValidator.php b/src/Wallabag/CoreBundle/Security/Validator/WallabagUserPasswordValidator.php
new file mode 100644
index 000000000..5586f9762
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Security/Validator/WallabagUserPasswordValidator.php
@@ -0,0 +1,48 @@
+securityContext = $securityContext;
+ $this->encoderFactory = $encoderFactory;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validate($password, Constraint $constraint)
+ {
+ if (!$constraint instanceof UserPassword) {
+ throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\UserPassword');
+ }
+
+ $user = $this->securityContext->getToken()->getUser();
+
+ if (!$user instanceof UserInterface) {
+ throw new ConstraintDefinitionException('The User object must implement the UserInterface interface.');
+ }
+
+ // give username, it's used to hash the password
+ $encoder = $this->encoderFactory->getEncoder($user);
+ $encoder->setUsername($user->getUsername());
+
+ if (!$encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) {
+ $this->context->addViolation($constraint->message);
+ }
+ }
+}
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php
index 30809a040..4aceed150 100644
--- a/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php
+++ b/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php
@@ -26,7 +26,8 @@ class ConfigControllerTest extends WallabagTestCase
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$this->assertCount(1, $crawler->filter('input[type=number]'));
- $this->assertCount(1, $crawler->filter('button[type=submit]'));
+ $this->assertCount(1, $crawler->filter('button[id=config_save]'));
+ $this->assertCount(1, $crawler->filter('button[id=change_passwd_save]'));
}
public function testUpdate()
@@ -38,7 +39,7 @@ class ConfigControllerTest extends WallabagTestCase
$this->assertEquals(200, $client->getResponse()->getStatusCode());
- $form = $crawler->filter('button[type=submit]')->form();
+ $form = $crawler->filter('button[id=config_save]')->form();
$data = array(
'config[theme]' => 'baggy',
@@ -84,7 +85,7 @@ class ConfigControllerTest extends WallabagTestCase
$this->assertEquals(200, $client->getResponse()->getStatusCode());
- $form = $crawler->filter('button[type=submit]')->form();
+ $form = $crawler->filter('button[id=config_save]')->form();
$crawler = $client->submit($form, $data);
@@ -93,4 +94,91 @@ class ConfigControllerTest extends WallabagTestCase
$this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text')));
$this->assertContains('This value should not be blank', $alert[0]);
}
+
+ public function dataForChangePasswordFailed()
+ {
+ return array(
+ array(
+ array(
+ 'change_passwd[old_password]' => 'baggy',
+ 'change_passwd[new_password][first]' => '',
+ 'change_passwd[new_password][second]' => '',
+ ),
+ 'Wrong value for your current password'
+ ),
+ array(
+ array(
+ 'change_passwd[old_password]' => 'mypassword',
+ 'change_passwd[new_password][first]' => '',
+ 'change_passwd[new_password][second]' => '',
+ ),
+ 'This value should not be blank'
+ ),
+ array(
+ array(
+ 'change_passwd[old_password]' => 'mypassword',
+ 'change_passwd[new_password][first]' => 'hop',
+ 'change_passwd[new_password][second]' => '',
+ ),
+ 'The password fields must match'
+ ),
+ array(
+ array(
+ 'change_passwd[old_password]' => 'mypassword',
+ 'change_passwd[new_password][first]' => 'hop',
+ 'change_passwd[new_password][second]' => 'hop',
+ ),
+ 'Password should by at least 6 chars long'
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider dataForChangePasswordFailed
+ */
+ public function testChangePasswordFailed($data, $expectedMessage)
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/config');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $form = $crawler->filter('button[id=change_passwd_save]')->form();
+
+ $crawler = $client->submit($form, $data);
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text')));
+ $this->assertContains($expectedMessage, $alert[0]);
+ }
+
+ public function testChangePassword()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/config');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $form = $crawler->filter('button[id=change_passwd_save]')->form();
+
+ $data = array(
+ 'change_passwd[old_password]' => 'mypassword',
+ 'change_passwd[new_password][first]' => 'mypassword',
+ 'change_passwd[new_password][second]' => 'mypassword',
+ );
+
+ $client->submit($form, $data);
+
+ $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+ $crawler = $client->followRedirect();
+
+ $this->assertGreaterThan(1, $alert = $crawler->filter('div.flash-notice')->extract(array('_text')));
+ $this->assertContains('Password updated', $alert[0]);
+ }
}
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php
index d77e23037..fcfa8ccf9 100644
--- a/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php
+++ b/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php
@@ -47,7 +47,7 @@ class WallabagRestControllerTest extends WallabagTestCase
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
- $headers = $this->generateHeaders('admin', 'test', $salt[0]);
+ $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
@@ -73,7 +73,7 @@ class WallabagRestControllerTest extends WallabagTestCase
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
- $headers = $this->generateHeaders('admin', 'test', $salt[0]);
+ $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
@@ -101,7 +101,7 @@ class WallabagRestControllerTest extends WallabagTestCase
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
- $headers = $this->generateHeaders('admin', 'test', $salt[0]);
+ $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$client->request('GET', '/api/entries', array(), array(), $headers);
@@ -125,7 +125,7 @@ class WallabagRestControllerTest extends WallabagTestCase
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
- $headers = $this->generateHeaders('admin', 'test', $salt[0]);
+ $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
diff --git a/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php b/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php
index 397945459..22016d8ed 100644
--- a/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php
+++ b/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php
@@ -24,7 +24,7 @@ abstract class WallabagTestCase extends WebTestCase
$form = $crawler->filter('button[type=submit]')->form();
$data = array(
'_username' => $username,
- '_password' => 'test',
+ '_password' => 'mypassword',
);
$this->client->submit($form, $data);