Skip to content

Commit

Permalink
feature #14153 [Admin][UI][API] Reset password validation (NoResponse…
Browse files Browse the repository at this point in the history
…Mate)

This PR was merged into the 1.12 branch.

Discussion
----------

| Q               | A                                                            |
|-----------------|--------------------------------------------------------------|
| Branch?         | 1.12          |
| Bug fix?        | no                                                       |
| New feature?    | yes                                                       |
| BC breaks?      | no                                                       |
| Deprecations?   | no <!-- don't forget to update the UPGRADE-*.md file --> |
| Related tickets | -                      |
| License         | MIT                                                          |

Based on #14147

Extracted it to not mix the concepts as the base PR only touches UI, but the validation is for both UI and API.

Commits
-------

6c1336a Add administrator password reset ui tests
ad5cf70 [Admin][UI] Rendering the reset password page
5f845b1 Move ResetPassword command and handler into Core
99dd0b2 [Admin][UI] Password resetting
f430bfb [Behat][Admin] Better elements' names in ResetPasswordPage
c37d52f [Admin][Core] Extract ResetPassword message dispatching to a standalone service
ea77ea0 [Behat] Fix admin's logging in logic
23ef23e [Behat][Admin] Fix and cleanup of password resetting
f6ce783 Fix sylius.admin.login.before_form event's priorities
53542d2 [Core] Remove "experimental" tag from ResetPassword related classes
ddbfa82 [Behat] More precise password changed notification check
0c596f6 [Admin][UI] Rework password reset template events
3ede1e5 [Admin][UI] Add completely forgotten password reset email link
5c320f0 [API][UI] Add validation for expired admin password reset token
fd24b11 [API][UI] Add password validation to admin's password reset workflow
  • Loading branch information
Zales0123 authored Jul 21, 2022
2 parents da8bda6 + fd24b11 commit 6193a33
Show file tree
Hide file tree
Showing 54 changed files with 1,064 additions and 59 deletions.
24 changes: 21 additions & 3 deletions features/admin/resetting_password.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Feature: Resetting an administrator's password
I want to be able to reset my password

Background:
Given there is an administrator "sylius@example.com" identified by "sylius"
Given the store operates on a single channel in "United States"
And there is an administrator "sylius@example.com" identified by "sylius"

@email @api @ui
Scenario: Sending an administrator's password reset request
Expand All @@ -23,7 +24,7 @@ Feature: Resetting an administrator's password
Then I should be notified that email with reset instruction has been sent
But "does-not-exist@example.com" should receive no emails

@api
@ui @api
Scenario: Changing my administrator's password
Given I have already received a resetting password email
When I follow the instructions to reset my password
Expand All @@ -33,11 +34,28 @@ Feature: Resetting an administrator's password
Then I should be notified that my password has been successfully changed
And I should be able to log in as "sylius@example.com" authenticated by "newp@ssw0rd" password

@api
@ui @api
Scenario: Trying to change my administrator's password twice without sending a new password reset request
Given I have already received an administrator's password resetting email
When I follow the instructions to reset my password
And I specify my new password as "newp@ssw0rd"
And I confirm my new password as "newp@ssw0rd"
And I reset it
Then I should not be able to change my password again with the same token

@api
Scenario: Trying to change my administrator's password using an expired reset token
Given I have already received an administrator's password resetting email
But my password reset token has already expired
When I try to follow the instructions to reset my password
And I specify my new password as "newp@ssw0rd"
And I confirm my new password as "newp@ssw0rd"
And I reset it
Then I should be notified that the password reset token has expired

@ui @no-api
Scenario: Trying to change my administrator's password using an expired reset token
Given I have already received an administrator's password resetting email
But my password reset token has already expired
When I try to follow the instructions to reset my password
Then I should be notified that the password reset token has expired
17 changes: 13 additions & 4 deletions features/admin/resetting_password_validation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,35 @@ Feature: Resetting an administrator's password validation
And I try to reset it
Then I should be notified that the email is required

@ui @api
Scenario: Trying to reset my administrator's password with an empty value
Given I have already received an administrator's password resetting email
When I follow the instructions to reset my password
And I do not specify my new password
And I do not confirm my new password
And I try to reset it
Then I should be notified that the new password is required

@ui @api
Scenario: Trying to reset my administrator's password with an invalid email
When I want to reset password
And I specify email as "sylius@examplecom"
And I try to reset it
Then I should be notified that the email is not valid

@todo
@ui @api
Scenario: Trying to reset my administrator's password with a wrong confirmation password
Given I have already received an administrator's password resetting email
When I follow link on my email to reset my password
When I follow the instructions to reset my password
And I specify my new password as "newp@ssw0rd"
And I confirm my new password as "wrongp@ssw0rd"
And I try to reset it
Then I should be notified that the entered passwords do not match

@todo
@ui @api
Scenario: Trying to reset my administrator's password with a too short password
Given I have already received an administrator's password resetting email
When I follow link on my email to reset my password
When I follow the instructions to reset my password
And I specify my new password as "fu"
And I confirm my new password as "fu"
And I try to reset it
Expand Down
51 changes: 48 additions & 3 deletions src/Sylius/Behat/Context/Api/Admin/ResettingPasswordContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public function iResetIt(): void
}

/**
* @When /^(I) follow the instructions to reset my password$/
* @When /^(I)(?:| try to) follow the instructions to reset my password$/
*/
public function iFollowTheInstructionsToResetMyPassword(AdminUserInterface $admin): void
{
Expand All @@ -74,16 +74,18 @@ public function iFollowTheInstructionsToResetMyPassword(AdminUserInterface $admi

/**
* @When I specify my new password as :password
* @When I do not specify my new password
*/
public function iSpecifyMyNewPassword(string $password): void
public function iSpecifyMyNewPassword(string $password = ''): void
{
$this->request->updateContent(['newPassword' => $password]);
}

/**
* @When I confirm my new password as :password
* @When I do not confirm my new password
*/
public function iConfirmMyNewPassword(string $password): void
public function iConfirmMyNewPassword(string $password = ''): void
{
$this->request->updateContent(['confirmNewPassword' => $password]);
}
Expand Down Expand Up @@ -139,4 +141,47 @@ public function iShouldBeNotifiedThatTheEmailIsNotValid(): void
'This email is not valid.'
);
}

/**
* @Then I should be notified that the password reset token has expired
*/
public function iShouldBeNotifiedThatThePasswordResetTokenHasExpired(): void
{
$message = $this->responseChecker->getError($this->client->getLastResponse());
Assert::same($message, 'The password reset token has expired.');
}

/**
* @Then I should be notified that the new password is required
*/
public function iShouldBeNotifiedThatTheNewPasswordIsRequired(): void
{
$this->assertResponseHasValidationMessageForNewPassword('Please enter the password.');
}

/**
* @Then I should be notified that the entered passwords do not match
*/
public function iShouldBeNotifiedThatTheEnteredPasswordsDoNotMatch(): void
{
$this->assertResponseHasValidationMessageForNewPassword('The entered passwords do not match.');
}

/**
* @Then /^I should be notified that the password should be ([^"]+)$/
*/
public function iShouldBeNotifiedThatThePasswordShouldBe(string $validationMessage): void
{
$this->assertResponseHasValidationMessageForNewPassword(sprintf('Password must be %s.', $validationMessage));
}

private function assertResponseHasValidationMessageForNewPassword(string $message): void
{
$lastResponse = $this->client->getLastResponse();

Assert::true(
$this->responseChecker->hasViolationWithMessage($lastResponse, $message, 'newPassword'),
$lastResponse->getContent(),
);
}
}
10 changes: 10 additions & 0 deletions src/Sylius/Behat/Context/Setup/AdminUserContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,14 @@ public function iHaveAlreadyReceivedAnAdministratorsPasswordResettingEmail(Admin

$this->objectManager->flush();
}

/**
* @Given /^(my) password reset token has already expired$/
*/
public function myPasswordResetTokenHasAlreadyExpired(AdminUserInterface $administrator): void
{
$administrator->setPasswordRequestedAt(new \DateTime('-1 year'));

$this->objectManager->flush();
}
}
34 changes: 14 additions & 20 deletions src/Sylius/Behat/Context/Ui/Admin/#Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,8 @@
namespace Sylius\Behat\Context\Ui\Admin;

use Behat\Behat\Context\Context;
use Sylius\Behat\NotificationType;
use Sylius\Behat\Page\Admin\Account\LoginPageInterface;
use Sylius\Behat\Page\Admin\Account\RequestPasswordResetPage;
use Sylius\Behat\Page\Admin\DashboardPageInterface;
use Sylius\Behat\Service\NotificationCheckerInterface;
use Sylius\Component\Core\Model\AdminUserInterface;
use Webmozart\Assert\Assert;

Expand Down Expand Up @@ -55,6 +52,14 @@ public function iSpecifyThePasswordAs($password = null)
$this->loginPage->specifyPassword($password);
}

/**
* @When /^(this administrator) logs in using "([^"]+)" password$/
*/
public function theyLogIn(AdminUserInterface $adminUser, $password)
{
$this->logInAgain($adminUser->getUsername(), $password);
}

/**
* @When I log in
*/
Expand Down Expand Up @@ -101,16 +106,7 @@ public function iShouldBeNotifiedAboutBadCredentials()
public function iShouldBeAbleToLogInAsAuthenticatedByPassword($username, $password)
{
$this->logInAgain($username, $password);

$this->dashboardPage->verify();
}

/**
* @When /^(this administrator) logs in using "([^"]+)" password$/
*/
public function theyLogIn(AdminUserInterface $adminUser, $password)
{
$this->logInAgain($adminUser->getUsername(), $password);
$this->iShouldBeLoggedIn();
}

/**
Expand All @@ -132,14 +128,12 @@ public function iShouldBeOnTheLoginPage(): void
Assert::true($this->loginPage->isOpen());
}

/**
* @param string $username
* @param string $password
*/
private function logInAgain($username, $password)
private function logInAgain(string $username, string $password): void
{
$this->dashboardPage->open();
$this->dashboardPage->logOut();
$this->dashboardPage->tryToOpen();
if ($this->dashboardPage->isOpen()) {
$this->dashboardPage->logOut();
}

$this->loginPage->open();
$this->loginPage->specifyUsername($username);
Expand Down
92 changes: 91 additions & 1 deletion src/Sylius/Behat/Context/Ui/Admin/ResettingPasswordContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,20 @@
namespace Sylius\Behat\Context\Ui\Admin;

use Behat\Behat\Context\Context;
use Sylius\Behat\Element\Admin\Account\ResetElementInterface;
use Sylius\Behat\NotificationType;
use Sylius\Behat\Page\Admin\Account\RequestPasswordResetPage;
use Sylius\Behat\Page\Admin\Account\ResetPasswordPageInterface;
use Sylius\Behat\Service\NotificationCheckerInterface;
use Sylius\Component\Core\Model\AdminUserInterface;
use Webmozart\Assert\Assert;

final class ResettingPasswordContext implements Context
{
public function __construct(
private RequestPasswordResetPage $requestPasswordResetPage,
private ResetPasswordPageInterface $resetPasswordPage,
private ResetElementInterface $resetElement,
private NotificationCheckerInterface $notificationChecker,
) {
}
Expand All @@ -49,7 +54,33 @@ public function iSpecifyEmailAs(string $email = ''): void
*/
public function iResetIt(): void
{
$this->requestPasswordResetPage->resetPassword();
$this->resetElement->reset();
}

/**
* @When /^(I)(?:| try to) follow the instructions to reset my password$/
*/
public function iFollowTheInstructionsToResetMyPassword(AdminUserInterface $admin): void
{
$this->resetPasswordPage->tryToOpen(['token' => $admin->getPasswordResetToken()]);
}

/**
* @When I specify my new password as :password
* @When I do not specify my new password
*/
public function iSpecifyMyNewPassword(string $password = ''): void
{
$this->resetPasswordPage->specifyNewPassword($password);
}

/**
* @When I confirm my new password as :password
* @When I do not confirm my new password
*/
public function iConfirmMyNewPassword(string $password = ''): void
{
$this->resetPasswordPage->specifyPasswordConfirmation($password);
}

/**
Expand Down Expand Up @@ -84,4 +115,63 @@ public function iShouldBeNotifiedThatTheEmailIsNotValid(): void
'This email is not valid.',
);
}

/**
* @Then I should be notified that my password has been successfully changed
*/
public function iShouldBeNotifiedThatMyPasswordHasBeenSuccessfullyChanged(): void
{
$this->notificationChecker->checkNotification('has been changed successfully!', NotificationType::success());
}

/**
* @Then I should not be able to change my password again with the same token
*/
public function iShouldNotBeAbleToChangeMyPasswordAgainWithTheSameToken(): void
{
$this->resetPasswordPage->tryToOpen(['token' => 'itotallyforgotmypassword']);

Assert::false($this->resetPasswordPage->isOpen(), 'User should not be on the forgotten password page');
}

/**
* @Then I should be notified that the password reset token has expired
*/
public function iShouldBeNotifiedThatThePasswordResetTokenHasExpired(): void
{
$this->notificationChecker->checkNotification('has expired', NotificationType::failure());
}

/**
* @Then I should be notified that the new password is required
*/
public function iShouldBeNotifiedThatTheNewPasswordIsRequired(): void
{
Assert::contains(
$this->resetPasswordPage->getValidationMessageFor('new_password'),
'Please enter the password.',
);
}

/**
* @Then I should be notified that the entered passwords do not match
*/
public function iShouldBeNotifiedThatTheEnteredPasswordsDoNotMatch(): void
{
Assert::contains(
$this->resetPasswordPage->getValidationMessageFor('new_password'),
'The entered passwords do not match.',
);
}

/**
* @Then /^I should be notified that the password should be ([^"]+)$/
*/
public function iShouldBeNotifiedThatThePasswordShouldBe(string $validationMessage): void
{
Assert::contains(
$this->resetPasswordPage->getValidationMessageFor('new_password'),
$validationMessage,
);
}
}
24 changes: 24 additions & 0 deletions src/Sylius/Behat/Element/Admin/Account/ResetElement.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Behat\Element\Admin\Account;

use FriendsOfBehat\PageObjectExtension\Element\Element;

final class ResetElement extends Element implements ResetElementInterface
{
public function reset(): void
{
$this->getDocument()->find('css', 'button[type="submit"]:contains("Reset")')->click();
}
}
Loading

0 comments on commit 6193a33

Please # to comment.