Skip to content

Commit

Permalink
Merge pull request #1800 from stloyd/symfony-6
Browse files Browse the repository at this point in the history
Maintain | Add support for Symfony 6
  • Loading branch information
stloyd committed Dec 7, 2021
2 parents 7128fd1 + d6569e3 commit 89d7868
Show file tree
Hide file tree
Showing 40 changed files with 1,041 additions and 605 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ Changelog
=========
## 2.0.0 (2021-xx-xx)
* BC Break: Dropped PHP 7.3 support,
* BC Break: Dropped support for Symfony: >=5.1 & <5.4,
* BC Break: `OAuthExtension` is now a lazy Twig extension using a Runtime,
* BC Break: removed support for `FOSUserBundle`,
* BC Break: changed `process()` argument for `Form/RegistrationFormHandlerInterface`, from `Form $form` to `FormInterface $form`,
Expand All @@ -19,6 +20,8 @@ Changelog
* BC Break: changed `__construct()` argument for `OAuth/ResourceOwner/AbstractResourceOwner`, from `HttpMethodsClient $httpClient` to `HttpClientInterface $httpClient`,
* BC Break: replaced `php-http/httplug-bundle` with `symfony/http-client`
* BC Break: removed `hwi_oauth.http` configuration
* Added support for PHP 8.1,
* Added support for Symfony 5.6,

## 1.4.2 (2021-08-09)
* Bugfix: remove `@final` declaration from `OAuthFactory` & `FOSUBUserProvider`,
Expand Down
111 changes: 68 additions & 43 deletions Controller/ConnectController.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,47 @@
use HWI\Bundle\OAuthBundle\Security\Core\Exception\AccountNotLinkedException;
use HWI\Bundle\OAuthBundle\Security\Http\ResourceOwnerMapLocator;
use HWI\Bundle\OAuthBundle\Security\OAuthUtils;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\Event as DeprecatedEvent;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Exception\AccountStatusException;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Contracts\EventDispatcher\Event;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Twig\Environment;

/**
* @author Alexander <iam.asm89@gmail.com>
*
* @internal
*/
final class ConnectController extends AbstractController
final class ConnectController
{
private OAuthUtils $oauthUtils;
private ResourceOwnerMapLocator $resourceOwnerMapLocator;
private RequestStack $requestStack;
private EventDispatcherInterface $dispatcher;
private TokenStorageInterface $tokenStorage;
private AccountConnectorInterface $accountConnector;
private UserCheckerInterface $userChecker;
private RegistrationFormHandlerInterface $formHandler;
private AuthorizationCheckerInterface $authorizationChecker;
private FormFactoryInterface $formFactory;
private Environment $twig;
private RouterInterface $router;
private bool $enableConnect;
private string $grantRule;
private bool $failedUseReferer;
Expand All @@ -64,6 +78,15 @@ public function __construct(
OAuthUtils $oauthUtils,
ResourceOwnerMapLocator $resourceOwnerMapLocator,
RequestStack $requestStack,
EventDispatcherInterface $dispatcher,
TokenStorageInterface $tokenStorage,
AccountConnectorInterface $accountConnector,
UserCheckerInterface $userChecker,
RegistrationFormHandlerInterface $formHandler,
AuthorizationCheckerInterface $authorizationChecker,
FormFactoryInterface $formFactory,
Environment $twig,
RouterInterface $router,
bool $enableConnect,
string $grantRule,
bool $failedUseReferer,
Expand All @@ -82,6 +105,15 @@ public function __construct(
$this->enableConnectConfirmation = $enableConnectConfirmation;
$this->firewallNames = $firewallNames;
$this->registrationForm = $registrationForm;
$this->dispatcher = $dispatcher;
$this->accountConnector = $accountConnector;
$this->tokenStorage = $tokenStorage;
$this->userChecker = $userChecker;
$this->formHandler = $formHandler;
$this->authorizationChecker = $authorizationChecker;
$this->formFactory = $formFactory;
$this->twig = $twig;
$this->router = $router;
}

/**
Expand All @@ -100,7 +132,7 @@ public function registrationAction(Request $request, string $key): Response
throw new NotFoundHttpException();
}

$hasUser = $this->isGranted($this->grantRule);
$hasUser = $this->authorizationChecker->isGranted($this->grantRule);
if ($hasUser) {
throw new AccessDeniedException('Cannot connect already registered account.');
}
Expand All @@ -124,28 +156,24 @@ public function registrationAction(Request $request, string $key): Response
->getUserInformation($error->getRawToken())
;

$form = $this->createForm($this->registrationForm);
$form = $this->formFactory->create($this->registrationForm);

/** @var RegistrationFormHandlerInterface $formHandler */
$formHandler = $this->get('hwi_oauth.registration.form.handler');
if ($formHandler->process($request, $form, $userInformation)) {
if ($this->formHandler->process($request, $form, $userInformation)) {
$event = new FormEvent($form, $request);
$this->dispatch($event, HWIOAuthEvents::REGISTRATION_SUCCESS);

/** @var AccountConnectorInterface $connector */
$connector = $this->get('hwi_oauth.account.connector');
$connector->connect($form->getData(), $userInformation);
$this->accountConnector->connect($form->getData(), $userInformation);

// Authenticate the user
$this->authenticateUser($request, $form->getData(), $error->getResourceOwnerName(), $error->getAccessToken());

if (null === $response = $event->getResponse()) {
if ($targetPath = $this->getTargetPath($session)) {
$response = $this->redirect($targetPath);
$response = new RedirectResponse($targetPath);
} else {
$response = $this->render('@HWIOAuth/Connect/registration_success.html.twig', [
$response = new Response($this->twig->render('@HWIOAuth/Connect/registration_success.html.twig', [
'userInformation' => $userInformation,
]);
]));
}
}

Expand All @@ -167,11 +195,11 @@ public function registrationAction(Request $request, string $key): Response
return $response;
}

return $this->render('@HWIOAuth/Connect/registration.html.twig', [
return new Response($this->twig->render('@HWIOAuth/Connect/registration.html.twig', [
'key' => $key,
'form' => $form->createView(),
'userInformation' => $userInformation,
]);
]));
}

/**
Expand All @@ -189,7 +217,7 @@ public function connectServiceAction(Request $request, string $service): Respons
throw new NotFoundHttpException();
}

$hasUser = $this->isGranted($this->grantRule);
$hasUser = $this->authorizationChecker->isGranted($this->grantRule);
if (!$hasUser) {
throw new AccessDeniedException('Cannot connect an account.');
}
Expand Down Expand Up @@ -222,38 +250,41 @@ public function connectServiceAction(Request $request, string $service): Respons
// Redirect to the login path if the token is empty (Eg. User cancelled auth)
if (null === $accessToken) {
if ($this->failedUseReferer && $targetPath = $this->getTargetPath($session)) {
return $this->redirect($targetPath);
return new RedirectResponse($targetPath);
}

return $this->redirectToRoute($this->failedAuthPath);
return new RedirectResponse($this->router->generate($this->failedAuthPath));
}

// Show confirmation page?
if (!$this->enableConnectConfirmation) {
return $this->getConfirmationResponse($request, $accessToken, $service);
}

$form = $this->createForm(FormType::class);
$form = $this->formFactory->create();
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
return $this->getConfirmationResponse($request, $accessToken, $service);
}

$event = new GetResponseUserEvent($this->getUser(), $request);
/** @var TokenInterface $token */
$token = $this->tokenStorage->getToken();

$event = new GetResponseUserEvent($token->getUser(), $request);

$this->dispatch($event, HWIOAuthEvents::CONNECT_INITIALIZE);

if ($response = $event->getResponse()) {
return $response;
}

return $this->render('@HWIOAuth/Connect/connect_confirm.html.twig', [
return new Response($this->twig->render('@HWIOAuth/Connect/connect_confirm.html.twig', [
'key' => $key,
'service' => $service,
'form' => $form->createView(),
'userInformation' => $resourceOwner->getUserInformation($accessToken),
]);
]));
}

/**
Expand Down Expand Up @@ -285,10 +316,8 @@ private function getResourceOwnerByName(string $name): ResourceOwnerInterface
private function authenticateUser(Request $request, UserInterface $user, string $resourceOwnerName, $accessToken, bool $fakeLogin = true): void
{
try {
/** @var UserCheckerInterface $userChecker */
$userChecker = $this->get('hwi_oauth.user_checker');
$userChecker->checkPreAuth($user);
$userChecker->checkPostAuth($user);
$this->userChecker->checkPreAuth($user);
$this->userChecker->checkPostAuth($user);
} catch (AccountStatusException $e) {
// Don't authenticate locked, disabled or expired users
return;
Expand All @@ -297,11 +326,13 @@ private function authenticateUser(Request $request, UserInterface $user, string
$token = new OAuthToken($accessToken, $user->getRoles());
$token->setResourceOwnerName($resourceOwnerName);
$token->setUser($user);
$token->setAuthenticated(true);

/** @var TokenStorageInterface $tokenStorage */
$tokenStorage = $this->get('security.token_storage');
$tokenStorage->setToken($token);
// required for compatibility with Symfony 5.4
if (method_exists($token, 'setAuthenticated')) {
$token->setAuthenticated(true, false);
}

$this->tokenStorage->setToken($token);

if ($fakeLogin) {
// Since we're "faking" normal login, we need to throw our INTERACTIVE_LOGIN event manually
Expand Down Expand Up @@ -335,11 +366,8 @@ private function getTargetPath(?SessionInterface $session): ?string
*/
private function getConfirmationResponse(Request $request, array $accessToken, string $service): Response
{
/** @var TokenStorageInterface $tokenStorage */
$tokenStorage = $this->get('security.token_storage');

/** @var OAuthToken $currentToken */
$currentToken = $tokenStorage->getToken();
$currentToken = $this->tokenStorage->getToken();
/** @var UserInterface $currentUser */
$currentUser = $currentToken->getUser();

Expand All @@ -349,14 +377,11 @@ private function getConfirmationResponse(Request $request, array $accessToken, s
$event = new GetResponseUserEvent($currentUser, $request);
$this->dispatch($event, HWIOAuthEvents::CONNECT_CONFIRMED);

/** @var AccountConnectorInterface $connector */
$connector = $this->get('hwi_oauth.account.connector');
$connector->connect($currentUser, $userInformation);
$this->accountConnector->connect($currentUser, $userInformation);

if ($currentToken instanceof OAuthToken) {
// Update user token with new details
$newToken =
\is_array($accessToken) &&
(isset($accessToken['access_token']) || isset($accessToken['oauth_token'])) ?
$accessToken : $currentToken->getRawToken();

Expand All @@ -365,12 +390,12 @@ private function getConfirmationResponse(Request $request, array $accessToken, s

if (null === $response = $event->getResponse()) {
if ($targetPath = $this->getTargetPath($request->getSession())) {
$response = $this->redirect($targetPath);
$response = new RedirectResponse($targetPath);
} else {
$response = $this->render('@HWIOAuth/Connect/connect_success.html.twig', [
$response = new Response($this->twig->render('@HWIOAuth/Connect/connect_success.html.twig', [
'userInformation' => $userInformation,
'service' => $service,
]);
]));
}
}

Expand All @@ -385,7 +410,7 @@ private function getConfirmationResponse(Request $request, array $accessToken, s
*/
private function dispatch($event, string $eventName = null): void
{
$this->get('event_dispatcher')->dispatch($event, $eventName);
$this->dispatcher->dispatch($event, $eventName);
}

private function getSession(): ?SessionInterface
Expand Down

0 comments on commit 89d7868

Please sign in to comment.