1515use Psr \Cache \CacheItemInterface ;
1616use Psr \Cache \InvalidArgumentException ;
1717use Symfony \Bundle \SecurityBundle \Security ;
18+ use Symfony \Component \HttpKernel \Exception \BadRequestHttpException ;
1819use Symfony \Component \HttpKernel \Exception \ConflictHttpException ;
1920use Symfony \Component \HttpKernel \Exception \ServiceUnavailableHttpException ;
2021use Symfony \Component \Security \Core \User \UserInterface ;
@@ -36,6 +37,7 @@ class InstantBook implements InteractiveSlideInterface
3637 private const string SCOPE = 'https://graph.microsoft.com/.default ' ;
3738 private const string GRANT_TYPE = 'password ' ;
3839 private const string CACHE_PREFIX = 'MS-INSTANT-BOOK ' ;
40+ private const string CACHE_ALLOWED_RESOURCES_PREFIX = 'INSTANT-BOOK-ALLOWED-RESOURCES- ' ;
3941 private const string CACHE_KEY_TOKEN_PREFIX = self ::CACHE_PREFIX .'-TOKEN- ' ;
4042 private const string CACHE_KEY_OPTIONS_PREFIX = self ::CACHE_PREFIX .'-OPTIONS- ' ;
4143 private const string CACHE_PREFIX_SPAM_PROTECT_PREFIX = self ::CACHE_PREFIX .'-SPAM-PROTECT- ' ;
@@ -75,6 +77,10 @@ public function getConfigOptions(): array
7577 'required ' => true ,
7678 'description ' => 'The key in the KeyVault for the password of the user. ' ,
7779 ],
80+ 'resourceEndpoint ' => [
81+ 'required ' => false ,
82+ 'description ' => 'The key in the KeyVault for the resources endpoint. This should supply a json list of resources that can be booked. The resources should have ResourceMail and allowInstantBooking ("True"/"False") properties set. ' ,
83+ ],
7884 ];
7985 }
8086
@@ -83,7 +89,7 @@ public function performAction(UserInterface $user, Slide $slide, InteractionSlid
8389 return match ($ interactionRequest ->action ) {
8490 self ::ACTION_GET_QUICK_BOOK_OPTIONS => $ this ->getQuickBookOptions ($ slide , $ interactionRequest ),
8591 self ::ACTION_QUICK_BOOK => $ this ->quickBook ($ slide , $ interactionRequest ),
86- default => throw new InteractiveSlideException ('Action not allowed ' ),
92+ default => throw new BadRequestHttpException ('Action not allowed ' ),
8793 };
8894 }
8995
@@ -98,7 +104,7 @@ private function authenticate(array $configuration): array
98104 $ password = $ this ->keyValueService ->getValue ($ configuration ['password ' ]);
99105
100106 if (4 !== count (array_filter ([$ tenantId , $ clientId , $ username , $ password ]))) {
101- throw new \ Exception ('tenantId, clientId, username, password must all be set. ' );
107+ throw new BadRequestHttpException ('tenantId, clientId, username, password must all be set. ' );
102108 }
103109
104110 $ url = self ::LOGIN_ENDPOINT .$ tenantId .self ::OAUTH_PATH ;
@@ -124,7 +130,7 @@ private function getToken(Tenant $tenant, InteractiveSlide $interactive): string
124130 $ configuration = $ interactive ->getConfiguration ();
125131
126132 if (null === $ configuration ) {
127- throw new \ Exception ( ' InteractiveNoConfiguration ' );
133+ throw new BadRequestHttpException ( ' Interactive no configuration ' );
128134 }
129135
130136 return $ this ->interactiveSlideCache ->get (
@@ -164,6 +170,9 @@ function (CacheItemInterface $item) use ($slide, $resource, $interactionRequest)
164170 throw new \Exception ('InteractiveNotFound ' );
165171 }
166172
173+ // Optional limiting of available resources.
174+ $ this ->checkPermission ($ interactive , $ resource );
175+
167176 $ feed = $ slide ->getFeed ();
168177
169178 if (null === $ feed ) {
@@ -273,25 +282,28 @@ function (CacheItemInterface $item) use ($now): \DateTime {
273282 $ interactive = $ this ->interactiveService ->getInteractiveSlide ($ tenant , $ interactionRequest ->implementationClass );
274283
275284 if (null === $ interactive ) {
276- throw new \ Exception ( ' InteractiveNotFound ' );
285+ throw new BadRequestHttpException ( ' Interactive not found ' );
277286 }
278287
288+ // Optional limiting of available resources.
289+ $ this ->checkPermission ($ interactive , $ resource );
290+
279291 $ feed = $ slide ->getFeed ();
280292
281293 if (null === $ feed ) {
282- throw new \ Exception ('Slide.feed not set. ' );
294+ throw new BadRequestHttpException ('Slide.feed not set. ' );
283295 }
284296
285297 if (!in_array ($ resource , $ feed ->getConfiguration ()['resources ' ] ?? [])) {
286- throw new \ Exception ('Resource not in feed resources ' );
298+ throw new BadRequestHttpException ('Resource not in feed resources ' );
287299 }
288300
289301 $ token = $ this ->getToken ($ tenant , $ interactive );
290302
291303 $ configuration = $ interactive ->getConfiguration ();
292304
293305 if (null === $ configuration ) {
294- throw new \ Exception ('InteractiveNoConfiguration ' );
306+ throw new BadRequestHttpException ('InteractiveNoConfiguration ' );
295307 }
296308
297309 $ username = $ this ->keyValueService ->getValue ($ configuration ['username ' ]);
@@ -411,13 +423,13 @@ private function getValueFromInterval(string $key, InteractionSlideRequest $inte
411423 $ interval = $ interactionRequest ->data ['interval ' ] ?? null ;
412424
413425 if (null === $ interval ) {
414- throw new \ Exception ('interval not set. ' );
426+ throw new BadRequestHttpException ('interval not set. ' );
415427 }
416428
417429 $ value = $ interval [$ key ] ?? null ;
418430
419431 if (null === $ value ) {
420- throw new \ Exception ("interval.'. $ key.' not set. " );
432+ throw new BadRequestHttpException ("interval.'. $ key.' not set. " );
421433 }
422434
423435 return $ value ;
@@ -431,4 +443,39 @@ private function getHeaders(string $token): array
431443 'Accept ' => 'application/json ' ,
432444 ];
433445 }
446+
447+ private function checkPermission (InteractiveSlide $ interactive , string $ resource ): void
448+ {
449+ // Optional limiting of available resources.
450+ if (!empty ($ interactive ->getConfiguration ()['resourceEndpoint ' ])) {
451+ $ allowedResources = $ this ->getAllowedResources ($ interactive );
452+
453+ if (!in_array ($ resource , $ allowedResources )) {
454+ throw new \Exception ('Not allowed ' );
455+ }
456+ }
457+ }
458+
459+ private function getAllowedResources (InteractiveSlide $ interactive ): array
460+ {
461+ return $ this ->interactiveSlideCache ->get (self ::CACHE_ALLOWED_RESOURCES_PREFIX . $ interactive ->getId (), function (CacheItemInterface $ item ) use ($ interactive ) {
462+ $ item ->expiresAfter (60 * 60 );
463+
464+ $ configuration = $ interactive ->getConfiguration ();
465+ $ resourceEndpoint = $ this ->keyValueService ->getValue ($ configuration ['resourceEndpoint ' ]);
466+
467+ $ response = $ this ->client ->request ('GET ' , $ resourceEndpoint );
468+ $ content = $ response ->toArray ();
469+
470+ $ allowedResources = [];
471+
472+ foreach ($ content as $ resource ) {
473+ if ($ resource ["allowInstantBooking " ] === 'True ' ) {
474+ $ allowedResources [] = $ resource ["ResourceMail " ];
475+ }
476+ }
477+
478+ return $ allowedResources ;
479+ });
480+ }
434481}
0 commit comments