Skip to content

Commit

Permalink
Merge pull request #4778 from BacLuc/activate-user-on-pw-reset
Browse files Browse the repository at this point in the history
ResetPasswordUpdateProcessor: also activate user after complete password reset process
  • Loading branch information
carlobeltrame committed Mar 16, 2024
2 parents 9fc485d + 499176c commit ede0e00
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 0 deletions.
4 changes: 4 additions & 0 deletions api/src/State/ResetPasswordUpdateProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\DTO\ResetPassword;
use App\Entity\User;
use App\Repository\UserRepository;
use App\Security\ReCaptcha\ReCaptchaWrapper;
use Doctrine\ORM\EntityManagerInterface;
Expand Down Expand Up @@ -46,6 +47,9 @@ public function process($data, Operation $operation, array $uriVariables = [], a
$passwordHasher = $this->pwHasherFactory->getPasswordHasher($user);
$user->password = $passwordHasher->hash($data->password);
$user->passwordResetKeyHash = null;
if (User::STATE_REGISTERED === $user->state) {
$user->state = User::STATE_ACTIVATED;
}
$this->em->flush();

$data->password = '';
Expand Down
63 changes: 63 additions & 0 deletions api/tests/Api/ResetPassword/UpdatePasswordTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
use App\Entity\User;
use App\Security\ReCaptcha\ReCaptchaWrapper;
use App\Tests\Api\ECampApiTestCase;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use ReCaptcha\Response;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;

/**
* @internal
Expand Down Expand Up @@ -96,6 +99,66 @@ public function testPatchResetPasswordValidatesUnreasonablyLongPassword() {
]);
}

/**
* @throws NonUniqueResultException
* @throws TransportExceptionInterface
* @throws NoResultException
*/
public function testPatchResetPasswordActivatesUser() {
$this->mockRecaptcha();
$this->getEntityManager()->createQueryBuilder()
->update(User::class, 'u')
->set('u.state', ':state')
->where('u.id = :id')
->setParameter('state', User::STATE_REGISTERED)
->setParameter('id', $this->user->getId())
->getQuery()
->execute()
;

$this->client->request(
'POST',
'/authentication_token',
[
'json' => [
'identifier' => $this->user->getEmail(),
'password' => 'test',
],
]
);

$this->assertResponseStatusCodeSame(401);

$newPassword = 'new_password';
$this->client->request(
'PATCH',
'/auth/reset_password/'.$this->passwordResetKey,
[
'json' => [
'password' => $newPassword,
],
'headers' => [
'Content-Type' => 'application/merge-patch+json',
],
]
);

$this->assertResponseStatusCodeSame(200);

$this->client->request(
'POST',
'/authentication_token',
[
'json' => [
'identifier' => $this->user->getEmail(),
'password' => $newPassword,
],
]
);

$this->assertResponseStatusCodeSame(204);
}

protected function mockRecaptcha($shouldReturnSuccess = true) {
$container = static::getContainer();
$recaptcha = $this->createMock(ReCaptchaWrapper::class);
Expand Down
39 changes: 39 additions & 0 deletions api/tests/State/ResetPasswordUpdateProcessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use App\Security\ReCaptcha\ReCaptchaWrapper;
use App\State\ResetPasswordUpdateProcessor;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\Attributes\TestWith;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use ReCaptcha\Response;
Expand Down Expand Up @@ -144,6 +145,7 @@ public function testUpdateWithCorrectResetKey() {
;
$user = new User();
$user->passwordResetKeyHash = 'resetKey';
$user->state = User::STATE_REGISTERED;

$this->userRepository->expects(self::once())
->method('loadUserByIdentifier')
Expand All @@ -167,5 +169,42 @@ public function testUpdateWithCorrectResetKey() {

self::assertThat($user->password, self::equalTo(md5('newPassword')));
self::assertThat($user->passwordResetKeyHash, self::isEmpty());
self::assertThat($user->state, self::equalTo(User::STATE_ACTIVATED));
}

#[TestWith([User::STATE_DELETED])]
#[TestWith([User::STATE_ACTIVATED])]
public function testUpdateWithCorrectResetKeyDoesNotChangeActivatedOrDeletedUsers(string $state) {
$this->recaptchaResponse->expects(self::once())
->method('isSuccess')
->willReturn(true)
;
$user = new User();
$user->passwordResetKeyHash = 'resetKey';
$user->state = $state;

$this->userRepository->expects(self::once())
->method('loadUserByIdentifier')
->with(self::EMAIL)
->willReturn($user)
;
$this->pwHasher->expects(self::once())
->method('verify')
->willReturn(true)
;
$this->pwHasher->expects(self::once())
->method('hash')
->willReturnCallback(fn ($raw) => md5($raw))
;

$this->resetPassword->id = base64_encode(self::EMAIL.'#myKey');
$this->resetPassword->recaptchaToken = 'token';
$this->resetPassword->password = 'newPassword';

$this->processor->process($this->resetPassword, new Patch());

self::assertThat($user->password, self::equalTo(md5('newPassword')));
self::assertThat($user->passwordResetKeyHash, self::isEmpty());
self::assertThat($user->state, self::equalTo($state));
}
}

0 comments on commit ede0e00

Please sign in to comment.