remote calendar: authenticate to ms graph api

This commit is contained in:
Julien Fastré 2022-05-01 23:44:18 +02:00
parent 0212193940
commit a7ec843509
13 changed files with 238 additions and 34 deletions

View File

@ -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",

View File

@ -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());
}
}

View File

@ -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';

View File

@ -0,0 +1,71 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\CalendarBundle\Controller;
use Chill\CalendarBundle\Synchro\Connector\MSGraph\MSGraphTokenStorage;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use TheNetworg\OAuth2\Client\Provider\Azure;
use TheNetworg\OAuth2\Client\Token\AccessToken;
class RemoteCalendarConnectAzureController
{
private ClientRegistry $clientRegistry;
private MSGraphTokenStorage $MSGraphTokenStorage;
public function __construct(
ClientRegistry $clientRegistry,
MSGraphTokenStorage $MSGraphTokenStorage
) {
$this->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', '/'));
}
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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/'

View File

@ -0,0 +1,42 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\CalendarBundle\Synchro\Connector\MSGraph;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use TheNetworg\OAuth2\Client\Token\AccessToken;
class MSGraphTokenStorage
{
public const MS_GRAPH_ACCESS_TOKEN = 'msgraph_access_token';
private SessionInterface $session;
public function __construct(SessionInterface $session)
{
$this->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);
}
}

View File

@ -0,0 +1,43 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\CalendarBundle\Synchro\Connector;
use Chill\CalendarBundle\Synchro\Connector\MSGraph\MSGraphTokenStorage;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
{
private MSGraphTokenStorage $MSGraphTokenStorage;
private UrlGeneratorInterface $urlGenerator;
public function __construct(
MSGraphTokenStorage $MSGraphTokenStorage,
UrlGeneratorInterface $urlGenerator
) {
$this->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();
}
}

View File

@ -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');
}

View File

@ -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

View File

@ -0,0 +1,52 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\CalendarBundle\Synchro\DependencyInjection;
use Chill\CalendarBundle\Synchro\Connector\MSGraphRemoteCalendarConnector;
use Chill\CalendarBundle\Synchro\Connector\NullRemoteCalendarConnector;
use Chill\CalendarBundle\Synchro\Connector\RemoteCalendarConnectorInterface;
use RuntimeException;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class RemoteCalendarCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$config = $container->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);
}
}
}
}

View File

@ -1,26 +0,0 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\CalendarBundle\Synchro\DependencyInjection;
use Chill\CalendarBundle\Synchro\Connector\NullRemoteCalendarConnector;
use Chill\CalendarBundle\Synchro\Connector\RemoteCalendarConnectorInterface;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class SynchroCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$nullConnector = $container->getDefinition(NullRemoteCalendarConnector::class);
$nullConnector->setDecoratedService(RemoteCalendarConnectorInterface::class);
}
}