Skip to content

Commit

Permalink
Merge pull request #108 from kbond/reduce-ui-deps
Browse files Browse the repository at this point in the history
Reduce UI deps
  • Loading branch information
kbond authored Nov 12, 2024
2 parents 4467faf + 6f54d74 commit 53d2787
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 22 deletions.
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,6 @@ bin/console messenger:monitor:schedule:purge --remove-orphans
## User Interface

> [!NOTE]
> `symfony/form` (`composer require symfony/form`) and `symfony/security-csrf`
> (`composer require symfony/security-csrf`) is required for the UI. CSRF protection
> and sessions must also be enabled in your `config/packages/framework.yaml`.
> [!NOTE]
> [Storage](#storage) must be configured for this feature.
Expand Down
4 changes: 1 addition & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@
"suggest": {
"knplabs/knp-time-bundle": "For human readable timestamps and durations on your dashboard.",
"lorisleiva/cron-translator": "For human readable cron expressions on your dashboard.",
"symfony/ux-live-component": "For Live Components",
"symfony/form": "For the UI",
"symfony/security-csrf": "For CSRF token used in UI Ajax calls"
"symfony/ux-live-component": "For Live Components"
},
"config": {
"preferred-install": "dist",
Expand Down
1 change: 1 addition & 0 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
service('zenstruck_messenger_monitor.history.storage')->nullOnInvalid(),
service('zenstruck_messenger_monitor.schedules')->nullOnInvalid(),
service('time.datetime_formatter')->nullOnInvalid(),
service('security.csrf.token_manager')->nullOnInvalid(),
])
->alias(ViewHelper::class, 'zenstruck_messenger_monitor.view_helper')
;
Expand Down
13 changes: 4 additions & 9 deletions src/Controller/MessengerMonitorController.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,7 @@ public function removeTransportMessage(
Request $request,
ViewHelper $helper,
): Response {
if (!$this->isCsrfTokenValid(\sprintf('remove-%s-%s', $id, $name), $request->request->getString('_token'))) {
throw new HttpException(419, 'Invalid CSRF token.');
}
$helper->validateCsrfToken($request->request->getString('_token'), 'remove', $id, $name);

$transport = $helper->transports->get($name);
$message = $transport->find($id) ?? throw $this->createNotFoundException('Message not found.');
Expand All @@ -178,9 +176,7 @@ public function retryFailedMessage(
ViewHelper $helper,
MessageBusInterface $bus,
): Response {
if (!$this->isCsrfTokenValid(\sprintf('retry-%s-%s', $id, $name), $request->request->getString('_token'))) {
throw new HttpException(419, 'Invalid CSRF token.');
}
$helper->validateCsrfToken($request->request->getString('_token'), 'retry', $id, $name);

$transport = $helper->transports->get($name);
$message = $transport->find($id) ?? throw $this->createNotFoundException('Message not found.');
Expand Down Expand Up @@ -241,10 +237,9 @@ public function triggerScheduleTask(
Request $request,
Schedules $schedules,
MessageBusInterface $bus,
ViewHelper $helper,
): Response {
if (!$this->isCsrfTokenValid(\sprintf('trigger-%s-%s', $id, $transport), $request->request->getString('_token'))) {
throw new HttpException(419, 'Invalid CSRF token.');
}
$helper->validateCsrfToken($request->request->getString('_token'), 'trigger', $id, $transport);

$task = $schedules->get($name)->task($id);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ private static function loadLiveComponents(ContainerBuilder $container, array $c

$container->setParameter('zenstruck_messenger_monitor.security_role', $config['role']);
$container->getDefinition('zenstruck_messenger_monitor.view_helper')
->setArgument(5, \interface_exists(AssetMapperInterface::class) ? ViewHelper::ASSET_MAPPER : ViewHelper::ENCORE)
->setArgument(6, \interface_exists(AssetMapperInterface::class) ? ViewHelper::ASSET_MAPPER : ViewHelper::ENCORE)
;
}
}
29 changes: 29 additions & 0 deletions src/Twig/ViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
namespace Zenstruck\Messenger\Monitor\Twig;

use Knp\Bundle\TimeBundle\DateTimeFormatter;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Zenstruck\Messenger\Monitor\History\Storage;
use Zenstruck\Messenger\Monitor\Schedules;
use Zenstruck\Messenger\Monitor\Transports;
Expand All @@ -36,6 +39,7 @@ public function __construct(
public readonly ?Storage $storage,
public readonly ?Schedules $schedules,
public readonly ?DateTimeFormatter $timeFormatter,
public readonly ?CsrfTokenManagerInterface $csrfTokenManager,
public readonly ?string $assetManager = null,
) {
}
Expand All @@ -49,4 +53,29 @@ public function canFormatDuration(): bool
{
return $this->timeFormatter && \method_exists($this->timeFormatter, 'formatDuration');
}

public function generateCsrfToken(string ...$parts): string
{
if (!$this->csrfTokenManager) {
return '';
}

return $this->csrfTokenManager->getToken(self::csrfTokenId(...$parts));
}

public function validateCsrfToken(string $token, string ...$parts): void
{
if (!$this->csrfTokenManager) {
return;
}

if (!$this->csrfTokenManager->isTokenValid(new CsrfToken(self::csrfTokenId(...$parts), $token))) {
throw new HttpException(419, 'Invalid CSRF token.');
}
}

private static function csrfTokenId(string ...$parts): string
{
return \implode('-', $parts);
}
}
4 changes: 2 additions & 2 deletions templates/schedule.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
{% endif %}

<a href="{{ path('zenstruck_messenger_monitor_schedule', {name: item.name}) }}" class="nav-link{{ item.name == schedule.name ? ' active' }}">
{{ item.name|humanize }} Schedule
{{ item.name|replace({'-' : ' ', '_': ' '})|title }} Schedule
</a>
</li>
{% endfor %}
Expand Down Expand Up @@ -166,7 +166,7 @@
</button>
<ul class="dropdown-menu" data-boundary="viewport">
{% for t in transports.names %}
<li><a class="dropdown-item post-link" data-token="{{ csrf_token(['trigger', task.id, t]|join('-')) }}" href="{{ path('zenstruck_messenger_monitor_schedule_trigger', {name: schedule.name, id: task.id, transport: t}) }}">{{ t }}</a></li>
<li><a class="dropdown-item post-link" data-token="{{ helper.generateCsrfToken('trigger', task.id, t) }}" href="{{ path('zenstruck_messenger_monitor_schedule_trigger', {name: schedule.name, id: task.id, transport: t}) }}">{{ t }}</a></li>
{% endfor %}
</ul>
</div>
Expand Down
4 changes: 2 additions & 2 deletions templates/transport.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,12 @@
</td>
{% endif %}
<td class="text-end">
<a href="{{ path('zenstruck_messenger_monitor_transport_remove', {name: transport.name, id: queued.id}) }}" class="post-link btn btn-sm btn-outline-danger" data-confirm="Are you sure?" data-token="{{ csrf_token(['remove', queued.id, transport.name]|join('-')) }}">
<a href="{{ path('zenstruck_messenger_monitor_transport_remove', {name: transport.name, id: queued.id}) }}" class="post-link btn btn-sm btn-outline-danger" data-confirm="Are you sure?" data-token="{{ helper.generateCsrfToken('remove', queued.id, transport.name) }}">
<svg fill="currentcolor" height="1em" width="1em" class="me-1 align-text-bottom" role="img" aria-label="Info:"><use xlink:href="#trash-icon"/></svg>
Remove
</a>
{% if transport.isFailure %}
<a href="{{ path('zenstruck_messenger_monitor_transport_retry', {name: transport.name, id: queued.id}) }}" class="post-link btn btn-sm btn-outline-secondary" data-token="{{ csrf_token(['retry', queued.id, transport.name]|join('-')) }}">
<a href="{{ path('zenstruck_messenger_monitor_transport_retry', {name: transport.name, id: queued.id}) }}" class="post-link btn btn-sm btn-outline-secondary" data-token="{{ helper.generateCsrfToken('retry', queued.id, transport.name) }}">
<svg fill="currentcolor" height="1em" width="1em" class="me-1 align-text-bottom" role="img" aria-label="Info:"><use xlink:href="#refresh-icon"/></svg>
Retry
</a>
Expand Down

0 comments on commit 53d2787

Please sign in to comment.