From a7ec843509bf57a1ba3f3bddcae31a406e4b4636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sun, 1 May 2022 23:44:18 +0200 Subject: [PATCH] remote calendar: authenticate to ms graph api --- composer.json | 2 + .../ChillCalendarBundle.php | 4 +- .../Controller/CalendarController.php | 2 +- .../RemoteCalendarConnectAzureController.php | 71 +++++++++++++++++++ .../ChillCalendarExtension.php | 2 + .../DependencyInjection/Configuration.php | 17 ++++- .../Resources/config/services/synchro.yaml | 7 ++ .../Connector/MSGraph/MSGraphTokenStorage.php | 42 +++++++++++ .../MSGraphRemoteCalendarConnector.php | 43 +++++++++++ .../Connector/NullRemoteCalendarConnector.php | 2 +- .../RemoteCalendarConnectorInterface.php | 2 +- .../RemoteCalendarCompilerPass.php | 52 ++++++++++++++ .../SynchroCompilerPass.php | 26 ------- 13 files changed, 238 insertions(+), 34 deletions(-) create mode 100644 src/Bundle/ChillCalendarBundle/Controller/RemoteCalendarConnectAzureController.php create mode 100644 src/Bundle/ChillCalendarBundle/Synchro/Connector/MSGraph/MSGraphTokenStorage.php create mode 100644 src/Bundle/ChillCalendarBundle/Synchro/Connector/MSGraphRemoteCalendarConnector.php create mode 100644 src/Bundle/ChillCalendarBundle/Synchro/DependencyInjection/RemoteCalendarCompilerPass.php delete mode 100644 src/Bundle/ChillCalendarBundle/Synchro/DependencyInjection/SynchroCompilerPass.php diff --git a/composer.json b/composer.json index d0fc51c0e..4c4169e1e 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "graylog2/gelf-php": "^1.5", "knplabs/knp-menu-bundle": "^3.0", "knplabs/knp-time-bundle": "^1.12", + "knpuniversity/oauth2-client-bundle": "^2.10", "league/csv": "^9.7.1", "nyholm/psr7": "^1.4", "ocramius/package-versions": "^1.10 || ^2", @@ -48,6 +49,7 @@ "symfony/webpack-encore-bundle": "^1.11", "symfony/workflow": "^4.4", "symfony/yaml": "^4.4", + "thenetworg/oauth2-azure": "^2.0", "twig/extra-bundle": "^3.0", "twig/intl-extra": "^3.0", "twig/markdown-extra": "^3.3", diff --git a/src/Bundle/ChillCalendarBundle/ChillCalendarBundle.php b/src/Bundle/ChillCalendarBundle/ChillCalendarBundle.php index 2e398afe7..28bf7b6d6 100644 --- a/src/Bundle/ChillCalendarBundle/ChillCalendarBundle.php +++ b/src/Bundle/ChillCalendarBundle/ChillCalendarBundle.php @@ -11,7 +11,7 @@ declare(strict_types=1); namespace Chill\CalendarBundle; -use Chill\CalendarBundle\Synchro\DependencyInjection\SynchroCompilerPass; +use Chill\CalendarBundle\Synchro\DependencyInjection\RemoteCalendarCompilerPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -21,6 +21,6 @@ class ChillCalendarBundle extends Bundle { parent::build($container); - $container->addCompilerPass(new SynchroCompilerPass()); + $container->addCompilerPass(new RemoteCalendarCompilerPass()); } } diff --git a/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php b/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php index 9af9ad1df..2f238e763 100644 --- a/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php +++ b/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php @@ -247,7 +247,7 @@ class CalendarController extends AbstractController } if (!$this->remoteCalendarConnector->isReady()) { - return $this->remoteCalendarConnector->getMakeReadyResponse(); + return $this->remoteCalendarConnector->getMakeReadyResponse($request->getUri()); } $view = '@ChillCalendar/Calendar/listByUser.html.twig'; diff --git a/src/Bundle/ChillCalendarBundle/Controller/RemoteCalendarConnectAzureController.php b/src/Bundle/ChillCalendarBundle/Controller/RemoteCalendarConnectAzureController.php new file mode 100644 index 000000000..1e73e6782 --- /dev/null +++ b/src/Bundle/ChillCalendarBundle/Controller/RemoteCalendarConnectAzureController.php @@ -0,0 +1,71 @@ +clientRegistry = $clientRegistry; + $this->MSGraphTokenStorage = $MSGraphTokenStorage; + } + + /** + * @Route("/{_locale}/connect/azure", name="chill_calendar_remote_connect_azure") + */ + public function connectAzure(Request $request): Response + { + $request->getSession()->set('azure_return_path', $request->query->get('returnPath', '/')); + + return $this->clientRegistry + ->getClient('azure') // key used in config/packages/knpu_oauth2_client.yaml + ->redirect([ + 'User.Read', 'Calendars.Read', 'Calendars.Read.Shared', + ]); + } + + /** + * @Route("/connect/azure/check", name="chill_calendar_remote_connect_azure_check") + */ + public function connectAzureCheck(Request $request): Response + { + /** @var Azure $client */ + $client = $this->clientRegistry->getClient('azure'); + + try { + /** @var AccessToken $token */ + $token = $client->getAccessToken(); + + $this->MSGraphTokenStorage->setToken($token); + } catch (IdentityProviderException $e) { + throw $e; + } + + return new RedirectResponse($request->getSession()->remove('azure_return_path', '/')); + } +} diff --git a/src/Bundle/ChillCalendarBundle/DependencyInjection/ChillCalendarExtension.php b/src/Bundle/ChillCalendarBundle/DependencyInjection/ChillCalendarExtension.php index d0bb84167..0f94c1982 100644 --- a/src/Bundle/ChillCalendarBundle/DependencyInjection/ChillCalendarExtension.php +++ b/src/Bundle/ChillCalendarBundle/DependencyInjection/ChillCalendarExtension.php @@ -37,6 +37,8 @@ class ChillCalendarExtension extends Extension implements PrependExtensionInterf $loader->load('services/form.yml'); $loader->load('services/event.yml'); $loader->load('services/synchro.yaml'); + + $container->setParameter('chill_calendar', $config); } public function prepend(ContainerBuilder $container) diff --git a/src/Bundle/ChillCalendarBundle/DependencyInjection/Configuration.php b/src/Bundle/ChillCalendarBundle/DependencyInjection/Configuration.php index f1e744cb8..7d5afba87 100644 --- a/src/Bundle/ChillCalendarBundle/DependencyInjection/Configuration.php +++ b/src/Bundle/ChillCalendarBundle/DependencyInjection/Configuration.php @@ -26,9 +26,20 @@ class Configuration implements ConfigurationInterface $treeBuilder = new TreeBuilder('chill_calendar'); $rootNode = $treeBuilder->getRootNode('chill_calendar'); - // Here you should define the parameters that are allowed to - // configure your bundle. See the documentation linked above for - // more information on that topic. + $rootNode->children() + ->arrayNode('remote_calendars_sync')->canBeEnabled() + ->children() + ->arrayNode('microsoft_graph')->canBeEnabled() + ->children() + ->scalarNode('machine_access_token') + ->isRequired() + ->info('Access token for writing to remote calendars') + ->end() // end of machine_access_token + ->end() // end of microsoft_graph children + ->end() // end of array microsoft_graph + ->end() // end of children's remote_calendars_sync + ->end() // end of array remote_calendars_sync + ->end(); return $treeBuilder; } diff --git a/src/Bundle/ChillCalendarBundle/Resources/config/services/synchro.yaml b/src/Bundle/ChillCalendarBundle/Resources/config/services/synchro.yaml index 7be6ccbec..f253f2a0f 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/config/services/synchro.yaml +++ b/src/Bundle/ChillCalendarBundle/Resources/config/services/synchro.yaml @@ -6,3 +6,10 @@ services: Chill\CalendarBundle\Synchro\Connector\RemoteCalendarConnectorInterface: ~ Chill\CalendarBundle\Synchro\Connector\NullRemoteCalendarConnector: ~ + + Chill\CalendarBundle\Synchro\Connector\MSGraphRemoteCalendarConnector: ~ + + Chill\CalendarBundle\Synchro\Connector\MSGraph\: + resource: '../../Synchro/Connector/MSGraph/' + + diff --git a/src/Bundle/ChillCalendarBundle/Synchro/Connector/MSGraph/MSGraphTokenStorage.php b/src/Bundle/ChillCalendarBundle/Synchro/Connector/MSGraph/MSGraphTokenStorage.php new file mode 100644 index 000000000..2448c9fbf --- /dev/null +++ b/src/Bundle/ChillCalendarBundle/Synchro/Connector/MSGraph/MSGraphTokenStorage.php @@ -0,0 +1,42 @@ +session = $session; + } + + public function getToken(): AccessToken + { + return $this->session->get(self::MS_GRAPH_ACCESS_TOKEN); + } + + public function hasToken(): bool + { + return $this->session->has(self::MS_GRAPH_ACCESS_TOKEN); + } + + public function setToken(AccessToken $token): void + { + $this->session->set(self::MS_GRAPH_ACCESS_TOKEN, $token); + } +} diff --git a/src/Bundle/ChillCalendarBundle/Synchro/Connector/MSGraphRemoteCalendarConnector.php b/src/Bundle/ChillCalendarBundle/Synchro/Connector/MSGraphRemoteCalendarConnector.php new file mode 100644 index 000000000..df6e21f8e --- /dev/null +++ b/src/Bundle/ChillCalendarBundle/Synchro/Connector/MSGraphRemoteCalendarConnector.php @@ -0,0 +1,43 @@ +MSGraphTokenStorage = $MSGraphTokenStorage; + $this->urlGenerator = $urlGenerator; + } + + public function getMakeReadyResponse(string $returnPath): Response + { + return new RedirectResponse($this->urlGenerator + ->generate('chill_calendar_remote_connect_azure', ['returnPath' => $returnPath])); + } + + public function isReady(): bool + { + return $this->MSGraphTokenStorage->hasToken(); + } +} diff --git a/src/Bundle/ChillCalendarBundle/Synchro/Connector/NullRemoteCalendarConnector.php b/src/Bundle/ChillCalendarBundle/Synchro/Connector/NullRemoteCalendarConnector.php index 3eb415e5d..9b64889c0 100644 --- a/src/Bundle/ChillCalendarBundle/Synchro/Connector/NullRemoteCalendarConnector.php +++ b/src/Bundle/ChillCalendarBundle/Synchro/Connector/NullRemoteCalendarConnector.php @@ -16,7 +16,7 @@ use Symfony\Component\HttpFoundation\Response; class NullRemoteCalendarConnector implements RemoteCalendarConnectorInterface { - public function getMakeReadyResponse(): Response + public function getMakeReadyResponse(string $returnPath): Response { throw new LogicException('As this connector is always ready, this method should not be called'); } diff --git a/src/Bundle/ChillCalendarBundle/Synchro/Connector/RemoteCalendarConnectorInterface.php b/src/Bundle/ChillCalendarBundle/Synchro/Connector/RemoteCalendarConnectorInterface.php index a0d5a3a19..b105fc81f 100644 --- a/src/Bundle/ChillCalendarBundle/Synchro/Connector/RemoteCalendarConnectorInterface.php +++ b/src/Bundle/ChillCalendarBundle/Synchro/Connector/RemoteCalendarConnectorInterface.php @@ -20,7 +20,7 @@ interface RemoteCalendarConnectorInterface * will be able to fullfill requirements to prepare this connector and * make it ready. */ - public function getMakeReadyResponse(): Response; + public function getMakeReadyResponse(string $returnPath): Response; /** * Return true if the connector is ready to act as a proxy for reading diff --git a/src/Bundle/ChillCalendarBundle/Synchro/DependencyInjection/RemoteCalendarCompilerPass.php b/src/Bundle/ChillCalendarBundle/Synchro/DependencyInjection/RemoteCalendarCompilerPass.php new file mode 100644 index 000000000..9c83bdebe --- /dev/null +++ b/src/Bundle/ChillCalendarBundle/Synchro/DependencyInjection/RemoteCalendarCompilerPass.php @@ -0,0 +1,52 @@ +getParameter('chill_calendar'); + $connector = null; + + if (!$config['remote_calendars_sync']['enabled']) { + $connector = NullRemoteCalendarConnector::class; + } else { + if ($config['remote_calendars_sync']['microsoft_graph']['enabled']) { + $connector = MSGraphRemoteCalendarConnector::class; + } + } + + if (null === $connector) { + throw new RuntimeException('Could not configure remote calendar'); + } + + foreach ([ + NullRemoteCalendarConnector::class, + MSGraphRemoteCalendarConnector::class, ] as $serviceId) { + if ($connector === $serviceId) { + $container->getDefinition($serviceId) + ->setDecoratedService(RemoteCalendarConnectorInterface::class); + } else { + // keep the container lighter by removing definitions + $container->removeDefinition($serviceId); + } + } + } +} diff --git a/src/Bundle/ChillCalendarBundle/Synchro/DependencyInjection/SynchroCompilerPass.php b/src/Bundle/ChillCalendarBundle/Synchro/DependencyInjection/SynchroCompilerPass.php deleted file mode 100644 index 37b70285a..000000000 --- a/src/Bundle/ChillCalendarBundle/Synchro/DependencyInjection/SynchroCompilerPass.php +++ /dev/null @@ -1,26 +0,0 @@ -getDefinition(NullRemoteCalendarConnector::class); - $nullConnector->setDecoratedService(RemoteCalendarConnectorInterface::class); - } -}