Skip to content

Commit

Permalink
new: Change user password
Browse files Browse the repository at this point in the history
  • Loading branch information
marien-probesys committed Jan 3, 2023
1 parent 6bbbbec commit d35f10e
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 4 deletions.
23 changes: 23 additions & 0 deletions assets/stylesheets/components/forms.css
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,22 @@ select[aria-invalid] {
border-color: var(--color-error11);
}

fieldset {
min-width: auto;
padding: 1rem;

background-color: var(--color-grey2);
border: 0.25rem solid var(--color-grey6);
border-radius: 0.5rem;
}

legend {
margin-right: auto;
margin-left: auto;
padding-right: 1rem;
padding-left: 1rem;
}

.tox-tinymce {
border-color: var(--color-grey8) !important;

Expand All @@ -228,6 +244,13 @@ select[aria-invalid] {
border-color: var(--color-primary8) !important;
}

.form__caption {
padding-right: 0.75rem;
padding-left: 0.75rem;

color: var(--color-grey11);
}

.form__error {
padding-left: 1.75em;

Expand Down
26 changes: 25 additions & 1 deletion src/Controller/ProfileController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Translation\TranslatableMessage;
use Symfony\Component\Validator\Validator\ValidatorInterface;

class ProfileController extends BaseController
Expand All @@ -30,8 +32,9 @@ public function edit(): Response
public function update(
Request $request,
UserRepository $userRepository,
UserPasswordHasherInterface $passwordHasher,
ValidatorInterface $validator,
RequestStack $requestStack
RequestStack $requestStack,
): Response {
/** @var \App\Entity\User $user */
$user = $this->getUser();
Expand All @@ -45,6 +48,12 @@ public function update(
/** @var string $email */
$email = $request->request->get('email', $initialEmail);

/** @var string $currentPassword */
$currentPassword = $request->request->get('currentPassword', '');

/** @var string $newPassword */
$newPassword = $request->request->get('newPassword', '');

/** @var string $csrfToken */
$csrfToken = $request->request->get('_csrf_token', '');

Expand All @@ -56,6 +65,21 @@ public function update(
]);
}

if ($newPassword) {
if (!$passwordHasher->isPasswordValid($user, $currentPassword)) {
return $this->renderBadRequest('profile/edit.html.twig', [
'name' => $name,
'email' => $email,
'errors' => [
'password' => new TranslatableMessage('The password is invalid.'),
],
]);
}

$newHashedPassword = $passwordHasher->hashPassword($user, $newPassword);
$user->setPassword($newHashedPassword);
}

$user->setName($name);
$user->setEmail($email);

Expand Down
49 changes: 47 additions & 2 deletions templates/profile/edit.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
{{ include('alerts/_error.html.twig', { message: error | trans }, with_context = false) }}
{% endif %}

<div class="text--info text--center">
<p class="text--info text--center">
{{ 'Note: the information of your profile can be visible to the other users.' | trans }}
</div>
</p>

<div class="flow-small">
<label for="name">
Expand Down Expand Up @@ -83,6 +83,51 @@
/>
</div>

<fieldset class="flow">
<legend>{{ 'Password' | trans }}</legend>

<p class="form__caption">
{{ 'Leave these fields blank to keep your current password.' | trans }}
</p>

<div class="flow-small">
<label for="current-password">
{{ 'Current password' | trans }}
</label>

{% if errors.password is defined %}
<p class="form__error" role="alert" id="current-password-error">
<span class="sr-only">{{ 'Error' | trans }}</span>
{{ errors.password }}
</p>
{% endif %}

<input
type="password"
id="current-password"
name="currentPassword"
autocomplete="current-password"
{% if errors.password is defined %}
aria-invalid="true"
aria-errormessage="current-password-error"
{% endif %}
/>
</div>

<div class="flow-small">
<label for="new-password">
{{ 'New password' | trans }}
</label>

<input
type="password"
id="new-password"
name="newPassword"
autocomplete="new-password"
/>
</div>
</fieldset>

<div class="form__actions">
<button id="form-update-profile-submit" class="button--primary" type="submit">
{{ 'Save the changes' | trans }}
Expand Down
48 changes: 48 additions & 0 deletions tests/Controller/ProfileControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,30 @@ public function testPostUpdateSavesTheUserAndRedirects(): void
$this->assertSame($newEmail, $user->getEmail());
}

public function testPostUpdateChangesThePassword(): void
{
$client = static::createClient();
/** @var \Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface */
$passwordHasher = self::getContainer()->get('security.user_password_hasher');
$initialPassword = Factory::faker()->unique()->password();
$newPassword = Factory::faker()->unique()->password();
$user = UserFactory::createOne([
'password' => $initialPassword,
]);
$client->loginUser($user->object());

$client->request('POST', '/profile', [
'_csrf_token' => $this->generateCsrfToken($client, 'update profile'),
'currentPassword' => $initialPassword,
'newPassword' => $newPassword,
]);

$this->assertResponseRedirects('/profile', 302);
$user->refresh();
$this->assertFalse($passwordHasher->isPasswordValid($user->object(), $initialPassword));
$this->assertTrue($passwordHasher->isPasswordValid($user->object(), $newPassword));
}

public function testPostUpdateFailsIfNameIsInvalid(): void
{
$client = static::createClient();
Expand Down Expand Up @@ -116,6 +140,30 @@ public function testPostUpdateFailsIfEmailIsInvalid(): void
$this->assertSame($initialEmail, $user->getEmail());
}

public function testPostUpdateFailsIfCurrentPasswordIsInvalid(): void
{
$client = static::createClient();
/** @var \Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface */
$passwordHasher = self::getContainer()->get('security.user_password_hasher');
$initialPassword = Factory::faker()->unique()->password();
$newPassword = Factory::faker()->unique()->password();
$user = UserFactory::createOne([
'password' => $initialPassword,
]);
$client->loginUser($user->object());

$client->request('POST', '/profile', [
'_csrf_token' => $this->generateCsrfToken($client, 'update profile'),
'currentPassword' => 'not the password',
'newPassword' => $newPassword,
]);

$this->assertSelectorTextContains('#current-password-error', 'The password is invalid.');
$user->refresh();
$this->assertTrue($passwordHasher->isPasswordValid($user->object(), $initialPassword));
$this->assertFalse($passwordHasher->isPasswordValid($user->object(), $newPassword));
}

public function testPostUpdateFailsIfCsrfTokenIsInvalid(): void
{
$client = static::createClient();
Expand Down
4 changes: 4 additions & 0 deletions translations/messages.en_GB.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,7 @@ Profile: Profile
'Note: the information of your profile can be visible to the other users.': 'Note: the information of your profile can be visible to the other users.'
Name: Name
'(optional, max. 100 characters)': '(optional, max. 100 characters)'
'The password is invalid.': 'The password is invalid.'
'Leave these fields blank to keep your current password.': 'Leave these fields blank to keep your current password.'
'Current password': 'Current password'
'New password': 'New password'
6 changes: 5 additions & 1 deletion translations/messages.fr_FR.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ Authors: Auteurs
'Bileto is developped by <a href="https://probesys.com">Probesys</a>.': 'Bileto est développé par <a href="https://probesys.com">Probesys</a>.'
'Close the notification': 'Fermer la notification'
Profile: Profil
'Note: the information of your profile can be visible to the other users.': 'Note : les informations de votre profil peuvent être visible par les autres utilisateurs.'
'Note: the information of your profile can be visible to the other users.': "Note\_: les informations de votre profil peuvent être visible par les autres utilisateurs."
Name: Nom
'(optional, max. 100 characters)': '(optionnel, max. 100 caractères)'
'The password is invalid.': 'Le mot de passe est invalide.'
'Leave these fields blank to keep your current password.': 'Laissez ces champs vides pour conserver votre mot de passe actuel.'
'Current password': 'Mot de passe actuel'
'New password': 'Nouveau mot de passe'

0 comments on commit d35f10e

Please sign in to comment.