Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UX] Allow to redirect logged-in user on any routes and add a redirection for registration page #15418

Open
wants to merge 10 commits into
base: 1.14
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Feature: Redirect when already signed in
In order to be aware that I am already logged in
As a Customer
I want to be redirected to account panel dashboard when accessing the login page
I want to be redirected to account panel dashboard when accessing the login page or the registration page

@ui @no-api
Scenario: Trying to access login page as logged in user
Expand All @@ -11,3 +11,11 @@ Feature: Redirect when already signed in
And I am logged in as "francis@underwood.com"
When I want to log in
Then I should be redirected to my account dashboard

@ui @no-api
Scenario: Trying to access registration page as logged in user
Given the store operates on a single channel in "United States"
And there is a customer "Francis Underwood" identified by an email "francis@underwood.com" and a password "whitehouse"
And I am logged in as "francis@underwood.com"
When I want to register a new account
Then I should be redirected to my account dashboard
15 changes: 15 additions & 0 deletions features/admin/redirect_when_already_logged_in.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@admin_dashboard
Feature: Redirect when already signed in
In order to be aware that I am already logged in
As an Administrator
I want to be redirected to the administration dashboard by using when accessing the login page

Background:
Given the store operates on a single channel in "United States"
And I am logged in as an administrator

@ui @no-api
Scenario: Trying to access login page as logged in administrator
When I want to log in
Then I should be redirected to the administration dashboard

15 changes: 13 additions & 2 deletions src/Sylius/Behat/Context/Ui/Admin/DashboardContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@

use Behat\Behat\Context\Context;
use FriendsOfBehat\PageObjectExtension\Page\UnexpectedPageException;
use Sylius\Behat\Page\Admin\Account\LoginPageInterface;
use Sylius\Behat\Page\Admin\DashboardPageInterface;
use Sylius\Component\Core\Model\ChannelInterface;
use Webmozart\Assert\Assert;

final class DashboardContext implements Context
{
public function __construct(private DashboardPageInterface $dashboardPage)
{
public function __construct(
private DashboardPageInterface $dashboardPage,
private LoginPageInterface $loginPage
){
}

/**
Expand Down Expand Up @@ -148,4 +151,12 @@ public function iShouldNotSeeTheAdministrationDashboard(): void
{
Assert::false($this->dashboardPage->isOpen());
}

/**
* @Then I should be redirected to the administration dashboard
*/
public function iShouldBeRedirectedToMyAccountDashboard(): void
{
Assert::true($this->dashboardPage->isOpen());
}
}
2 changes: 1 addition & 1 deletion src/Sylius/Behat/Context/Ui/Admin/LoginContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function __construct(
*/
public function iWantToLogIn()
{
$this->loginPage->open();
$this->loginPage->tryToOpen();
}

/**
Expand Down
11 changes: 11 additions & 0 deletions src/Sylius/Behat/Context/Ui/Shop/AccountContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Sylius\Behat\Page\Shop\Account\Order\IndexPageInterface;
use Sylius\Behat\Page\Shop\Account\Order\ShowPageInterface;
use Sylius\Behat\Page\Shop\Account\ProfileUpdatePageInterface;
use Sylius\Behat\Page\Shop\Account\RegisterPageInterface;
use Sylius\Behat\Service\NotificationCheckerInterface;
use Sylius\Behat\Service\SharedStorageInterface;
use Sylius\Component\Core\Formatter\StringInflector;
Expand All @@ -40,6 +41,7 @@ public function __construct(
private LoginPageInterface $loginPage,
private NotificationCheckerInterface $notificationChecker,
private SharedStorageInterface $sharedStorage,
private RegisterPageInterface $registerPage,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tell me if we should gather PageInterface together in order's declaration

) {
}

Expand Down Expand Up @@ -464,6 +466,15 @@ public function iWantToLogIn(): void
$this->loginPage->tryToOpen();
}

/**
* @When /^I want to(?:| again) register a new account$/
*/
public function iWantToRegisterANewAccount(): void
{
$this->registerPage->tryToOpen();
}


/**
* @Then I should see its payment status as :paymentStatus
*/
Expand Down
2 changes: 2 additions & 0 deletions src/Sylius/Behat/Resources/config/services/contexts/ui.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

<service id="sylius.behat.context.ui.admin.dashboard" class="Sylius\Behat\Context\Ui\Admin\DashboardContext">
<argument type="service" id="sylius.behat.page.admin.dashboard" />
<argument type="service" id="sylius.behat.page.admin.login" />
</service>

<service id="sylius.behat.context.ui.admin.locale" class="Sylius\Behat\Context\Ui\Admin\LocaleContext">
Expand Down Expand Up @@ -461,6 +462,7 @@
<argument type="service" id="sylius.behat.page.shop.account.login" />
<argument type="service" id="sylius.behat.notification_checker" />
<argument type="service" id="sylius.behat.shared_storage" />
<argument type="service" id="sylius.behat.page.shop.account.register" />
</service>

<service id="sylius.behat.context.ui.shop.address_book" class="Sylius\Behat\Context\Ui\Shop\AddressBookContext">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ sylius_shop_register:
redirect:
route: sylius_shop_register_thank_you
flash: sylius.customer.register
logged_in_route: sylius_shop_account_dashboard

sylius_shop_register_after_checkout:
path: /register-after-checkout/{tokenValue}
Expand Down
11 changes: 0 additions & 11 deletions src/Sylius/Bundle/UiBundle/Controller/SecurityController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,8 @@

use Sylius\Bundle\UiBundle\Form\Type\SecurityLoginType;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Twig\Environment;

Expand All @@ -29,19 +26,11 @@ public function __construct(
private AuthenticationUtils $authenticationUtils,
private FormFactoryInterface $formFactory,
private Environment $templatingEngine,
private AuthorizationCheckerInterface $authorizationChecker,
private RouterInterface $router,
) {
}

public function loginAction(Request $request): Response
{
$alreadyLoggedInRedirectRoute = $request->attributes->get('_sylius', [])['logged_in_route'] ?? null;

if ($alreadyLoggedInRedirectRoute && $this->authorizationChecker->isGranted('IS_AUTHENTICATED_FULLY')) {
return new RedirectResponse($this->router->generate($alreadyLoggedInRedirectRoute));
}

$lastError = $this->authenticationUtils->getLastAuthenticationError();
$lastUsername = $this->authenticationUtils->getLastUsername();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* 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\UiBundle\EventListener;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;

/**
* Allow to redirect already logged-in user to a specific route.
*
* To do so, add in your route configuration a 'logged_in_route' attribute under the '_sylius' key.
* You can provide directly the route name or an array with 'name' and 'parameters' keys.
*/
final class AlreadyLoggedInUserRedirectionListener
{
public function __construct(
private AuthorizationCheckerInterface $authorizationChecker,
private RequestStack $requestStack,
private RouterInterface $router
) {
}

public function handleAlreadyConnectedUser(RequestEvent $event): void
{
if (!$this->authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
return;
}

$alreadyLoggedInRedirectRoute = $this->requestStack->getMainRequest()->attributes->get('_sylius')['logged_in_route'] ?? null;
if (null === $alreadyLoggedInRedirectRoute) {
return;
} elseif (is_string($alreadyLoggedInRedirectRoute)) {
// case logged_in_route: 'app_route'
$event->setResponse(new RedirectResponse($this->router->generate($alreadyLoggedInRedirectRoute)));
return;
}

// case logged_in_route:
// name: 'app_route'
// parameters:
// param1: 'value1'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tell me if such comment are not needed

$event->setResponse(new RedirectResponse($this->router->generate(
$alreadyLoggedInRedirectRoute['name'] ?? throw new \InvalidArgumentException('The "name" key must be set for the "logged_in_route" attribute.'),
$alreadyLoggedInRedirectRoute['parameters'] ?? []
)));
}
}
1 change: 1 addition & 0 deletions src/Sylius/Bundle/UiBundle/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<import resource="services/context_provider.xml" />
<import resource="services/controller.xml" />
<import resource="services/form.xml" />
<import resource="services/listeners.xml" />
<import resource="services/template_event.xml" />
<import resource="services/twig.xml" />
</imports>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--

This file is part of the Sylius package.

(c) Sylius Sp. z o.o.

For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.

-->

<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<defaults public="true" />
<service id="sylius.listener.already_logged_in_user_redirection" class="Sylius\Bundle\UiBundle\EventListener\AlreadyLoggedInUserRedirectionListener">
<argument type="service" id="security.authorization_checker"/>
<argument type="service" id="request_stack" />
<argument type="service" id="router" />
<tag name="kernel.event_listener" event="kernel.request" method="handleAlreadyConnectedUser" />
</service>
</services>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -75,20 +75,6 @@ function it_renders_login_form(
$this->loginAction($request)->getContent()->shouldReturn('content');
}

function it_redirects_when_user_is_logged_in(
Request $request,
ParameterBag $requestAttributes,
AuthorizationCheckerInterface $authorizationChecker,
RouterInterface $router,
): void {
$request->attributes = $requestAttributes;
$requestAttributes->get('_sylius', [])->willReturn(['logged_in_route' => 'foo_bar']);
$authorizationChecker->isGranted('IS_AUTHENTICATED_FULLY')->willReturn(true);
$router->generate('foo_bar')->willReturn('/login');

$this->loginAction($request)->shouldHaveType(RedirectResponse::class);
}

function it_throws_an_exception_when_check_action_is_accessed(Request $request): void
{
$this
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace spec\Sylius\Bundle\UiBundle\EventListener;

use PhpSpec\ObjectBehavior;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;


final class AlreadyLoggedInUserRedirectionListenerSpec extends ObjectBehavior
{
function let(
AuthorizationCheckerInterface $authorizationChecker,
RequestStack $requestStack,
RouterInterface $router
): void {
$this->beConstructedWith($authorizationChecker, $requestStack, $router);
}

function it_add_response_to_event_when_user_is_already_logged_in(
RequestEvent $event,
Request $request,
ParameterBag $requestAttributes,
AuthorizationCheckerInterface $authorizationChecker,
RequestStack $requestStack,
RouterInterface $router
): void {
$authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')->willReturn(true);

$requestStack->getMainRequest()->willReturn($request);
$request->attributes = $requestAttributes;
$requestAttributes->get('_sylius')->willReturn(['logged_in_route' => 'app_route']);

$router->generate('app_route')->willReturn('/app_route');

$event->setResponse(new RedirectResponse('/app_route'))->shouldBeCalled();

$this->handleAlreadyConnectedUser($event);
}

function it_add_response_to_event_when_user_is_already_logged_in_with_parameters(
RequestEvent $event,
Request $request,
ParameterBag $requestAttributes,
AuthorizationCheckerInterface $authorizationChecker,
RequestStack $requestStack,
RouterInterface $router
): void {
$authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')->willReturn(true);

$requestStack->getMainRequest()->willReturn($request);
$request->attributes = $requestAttributes;
$requestAttributes->get('_sylius')->willReturn([
'logged_in_route' => [
'name' => 'app_route',
'parameters' => ['param1' => 'value1']
]
]);

$router->generate('app_route', ['param1' => 'value1'])->willReturn('/app_route?param1=value1');

$event->setResponse(new RedirectResponse('/app_route?param1=value1'))->shouldBeCalled();

$this->handleAlreadyConnectedUser($event);
}

function it_does_not_alter_event_when_not_logged_in(
RequestEvent $event,
AuthorizationCheckerInterface $authorizationChecker
): void {
$authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')->willReturn(false);

$event->setResponse(new RedirectResponse('/app_route'))->shouldNotBeCalled();

$this->handleAlreadyConnectedUser($event);
}
}
Loading