Skip to content
Permalink
Browse files Browse the repository at this point in the history
[Shop] Disabling customer when email has been changed
  • Loading branch information
lchrusciel committed Oct 19, 2020
1 parent ddb5fa3 commit 60636d7
Show file tree
Hide file tree
Showing 4 changed files with 291 additions and 1 deletion.
Expand Up @@ -19,9 +19,21 @@ Feature: Editing a customer profile
And my name should be "Will Conway"

@ui
Scenario: Changing my email
Scenario: Changing my email if channel requires verification
When I want to modify my profile
And I specify the customer email as "frank@underwood.com"
And I save my changes
Then I should be notified that it has been successfully edited
And I should be notified that the verification email has been sent
And it should be sent to "frank@underwood.com"
And I should not be logged in

@ui
Scenario: Changing my email if channel does not require verification
Given "United States" channel has account verification disabled
When I want to modify my profile
And I specify the customer email as "frank@underwood.com"
And I save my changes
Then I should be notified that it has been successfully edited
And my account should not be verified
And my email should be "frank@underwood.com"
163 changes: 163 additions & 0 deletions src/Sylius/Behat/Context/Ui/Shop/VerificationContext.php
@@ -0,0 +1,163 @@
<?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\Context\Ui\Shop;

use Behat\Behat\Context\Context;
use Sylius\Behat\NotificationType;
use Sylius\Behat\Page\Shop\Account\DashboardPageInterface;
use Sylius\Behat\Page\Shop\Account\VerificationPageInterface;
use Sylius\Behat\Service\NotificationCheckerInterface;
use Sylius\Behat\Service\SharedStorageInterface;
use Sylius\Component\Core\Model\CustomerInterface;
use Sylius\Component\Core\Model\ShopUserInterface;
use Webmozart\Assert\Assert;

class VerificationContext implements Context
{
/** @var SharedStorageInterface */
private $sharedStorage;

/** @var DashboardPageInterface */
private $dashboardPage;

/** @var VerificationPageInterface */
private $verificationPage;

/** @var NotificationCheckerInterface */
private $notificationChecker;

public function __construct(
SharedStorageInterface $sharedStorage,
DashboardPageInterface $dashboardPage,
VerificationPageInterface $verificationPage,
NotificationCheckerInterface $notificationChecker
) {
$this->sharedStorage = $sharedStorage;
$this->dashboardPage = $dashboardPage;
$this->verificationPage = $verificationPage;
$this->notificationChecker = $notificationChecker;
}

/**
* @Then I should be notified that my account has been created and the verification email has been sent
*/
public function iShouldBeNotifiedThatNewAccountHasBeenSuccessfullyCreated(): void
{
$this->notificationChecker->checkNotification(
'Thank you for registering, check your email to verify your account.',
NotificationType::success()
);
}

/**
* @Then /^my account should be verified$/
*/
public function myAccountShouldBeVerified(): void
{
Assert::true($this->dashboardPage->isVerified());
}

/**
* @When /^(I) try to verify my account using the link from this email$/
*/
public function iUseItToVerify(ShopUserInterface $user): void
{
$this->verificationPage->verifyAccount($user->getEmailVerificationToken());
}

/**
* @When I verify my account using link sent to :customer
*/
public function iVerifyMyAccount(CustomerInterface $customer): void
{
$user = $customer->getUser();
Assert::notNull($user, 'No account for given customer');

$this->iUseItToVerify($user);
}

/**
* @When I resend the verification email
*/
public function iResendVerificationEmail(): void
{
$this->dashboardPage->open();
$this->dashboardPage->pressResendVerificationEmail();
}

/**
* @When I use the verification link from the first email to verify
*/
public function iUseVerificationLinkFromFirstEmailToVerify(): void
{
$token = $this->sharedStorage->get('verification_token');

$this->verificationPage->verifyAccount($token);
}

/**
* @When I (try to )verify using :token token
*/
public function iTryToVerifyUsing(string $token): void
{
$this->verificationPage->verifyAccount($token);
}

/**
* @Then /^(?:my|his|her) account should not be verified$/
*/
public function myAccountShouldNotBeVerified(): void
{
$this->dashboardPage->open();

Assert::false($this->dashboardPage->isVerified());
}

/**
* @Then I should not be able to resend the verification email
*/
public function iShouldBeUnableToResendVerificationEmail(): void
{
$this->dashboardPage->open();

Assert::false($this->dashboardPage->hasResendVerificationEmailButton());
}

/**
* @Then I should be notified that the verification was successful
*/
public function iShouldBeNotifiedThatTheVerificationWasSuccessful(): void
{
$this->notificationChecker->checkNotification('has been successfully verified.', NotificationType::success());
}

/**
* @Then I should be notified that the verification token is invalid
*/
public function iShouldBeNotifiedThatTheVerificationTokenIsInvalid(): void
{
$this->notificationChecker->checkNotification('The verification token is invalid.', NotificationType::failure());
}

/**
* @Then I should be notified that the verification email has been sent
*/
public function iShouldBeNotifiedThatTheVerificationEmailHasBeenSent(): void
{
$this->notificationChecker->checkNotification(
'An email with the verification link has been sent to your email address.',
NotificationType::success()
);
}
}
@@ -0,0 +1,106 @@
<?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\Bundle\ShopBundle\EventListener;

use Sylius\Bundle\UserBundle\EventListener\PasswordUpdaterListener as BasePasswordUpdaterListener;
use Sylius\Bundle\UserBundle\UserEvents;
use Sylius\Component\Channel\Context\ChannelContextInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\CustomerInterface;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\Component\User\Security\Generator\GeneratorInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Webmozart\Assert\Assert;

final class CustomerEmailUpdaterListener
{
/** @var GeneratorInterface */
private $tokenGenerator;
/** @var ChannelContextInterface */
private $channelContext;
/** @var EventDispatcherInterface */
private $eventDispatcher;
/** @var SessionInterface */
private $session;

public function __construct(
GeneratorInterface $tokenGenerator,
ChannelContextInterface $channelContext,
EventDispatcherInterface $eventDispatcher,
SessionInterface $session
) {
$this->tokenGenerator = $tokenGenerator;
$this->channelContext = $channelContext;
$this->eventDispatcher = $eventDispatcher;
$this->session = $session;
}

public function eraseVerification(GenericEvent $event): void
{
$customer = $event->getSubject();

/** @var CustomerInterface $customer */
Assert::isInstanceOf($customer, CustomerInterface::class);

/** @var ShopUserInterface $user */
$user = $customer->getUser();
if ($customer->getEmail() !== $user->getUsername()) {
$user->setEmail($customer->getEmail());
$user->setVerifiedAt(null);

$token = $this->tokenGenerator->generate();
$user->setEmailVerificationToken($token);

/** @var ChannelInterface $channel */
$channel = $this->channelContext->getChannel();

if ($channel->isAccountVerificationRequired()) {
$user->setEnabled(false);
}
}
}

public function sendVerificationEmail(GenericEvent $event): void
{
/** @var ChannelInterface $channel */
$channel = $this->channelContext->getChannel();

if (!$channel->isAccountVerificationRequired()) {
return;
}

$customer = $event->getSubject();

/** @var CustomerInterface $customer */
Assert::isInstanceOf($customer, CustomerInterface::class);

/** @var ShopUserInterface $user */
$user = $customer->getUser();

if (!$user->isEnabled() && !$user->isVerified() && null !== $user->getEmailVerificationToken()) {
$this->eventDispatcher->dispatch(UserEvents::REQUEST_VERIFICATION_TOKEN, new GenericEvent($user));
$this->addFlash('success', 'sylius.user.verify_email_request');
}
}

private function addFlash(string $type, string $message): void
{
/** @var FlashBagInterface $flashBag */
$flashBag = $this->session->getBag('flashes');
$flashBag->add($type, $message);
}
}
9 changes: 9 additions & 0 deletions src/Sylius/Bundle/ShopBundle/Resources/config/services.xml
Expand Up @@ -29,6 +29,15 @@
<argument type="service" id="sylius.storage.cart_session" />
</service>

<service id="sylius.listener.email_updater" class="Sylius\Bundle\ShopBundle\EventListener\CustomerEmailUpdaterListener">
<argument type="service" id="sylius.shop_user.token_generator.email_verification" />
<argument type="service" id="sylius.context.channel" />
<argument type="service" id="event_dispatcher" />
<argument type="service" id="session" />
<tag name="kernel.event_listener" event="sylius.customer.pre_update" method="eraseVerification" />
<tag name="kernel.event_listener" event="sylius.customer.post_update" method="sendVerificationEmail" />
</service>

<service id="sylius.context.cart.session_and_channel_based" class="Sylius\Bundle\CoreBundle\Context\SessionAndChannelBasedCartContext">
<argument type="service" id="sylius.storage.cart_session" />
<argument type="service" id="sylius.context.channel" />
Expand Down

0 comments on commit 60636d7

Please sign in to comment.