Skip to content

Commit 459dfb3

Browse files
committed
4680: Added resource endpoint for limiting access to instant book interactive slide
1 parent 4e32570 commit 459dfb3

File tree

2 files changed

+58
-9
lines changed

2 files changed

+58
-9
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
- [#244](https://github.com/os2display/display-api-service/pull/244)
8+
- Added resource endpoint for limiting access to instant book interactive slide.
79
- [#243](https://github.com/os2display/display-api-service/pull/243)
810
- Changed resource name in calendar api feed type resource selector.
911
- [#242](https://github.com/os2display/display-api-service/pull/242)

src/InteractiveSlide/InstantBook.php

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Psr\Cache\CacheItemInterface;
1616
use Psr\Cache\InvalidArgumentException;
1717
use Symfony\Bundle\SecurityBundle\Security;
18+
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
1819
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
1920
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
2021
use 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

Comments
 (0)