Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Latest
* [#150](https://github.com/cleverage/ui-process-bundle/issues/150) Require `symfony/monolog-bundle` and `monolog/monolog`, used directly by the bundle (log handlers, `monolog` configuration prepended, `monolog.logger` tags) but only installed through `cleverage/process-bundle`, which may stop requiring MonologBundle (cleverage/process-bundle#269).

## Fixes
* [#173](https://github.com/cleverage/ui-process-bundle/issues/173) Fix the hard-coded URLs: the page displayed after the login is generated from the dashboard route (was `/process`: wrong when the routes are imported with a `prefix`, or the application is served from a sub-directory), and the logo URL is built by the Asset component (base path, `framework.assets.base_urls`, version strategy; absolute URLs kept as is; was `/<logo_path>`). New `ProcessDashboardController` constructor argument (`Symfony\Component\Asset\Packages`, autowired). Require `symfony/asset`, used directly. Add tests.
* [#171](https://github.com/cleverage/ui-process-bundle/issues/171) Configuration: `config:dump-reference clever_age_ui_process` and `debug:config clever_age_ui_process` no longer fail (`CleverAgeUiProcessExtension::getConfiguration()` gives the kernel environment to `Configuration`, so the PHP config builder is generated too); the options are documented with `info()`. The `logs.database_level`, `logs.file_level` and `logs.report_increment_level` options are validated when the container is compiled, an invalid level no longer makes every process execution fail at runtime (environment variables are still allowed, checked at runtime). Update documentation, add tests.
* [#169](https://github.com/cleverage/ui-process-bundle/issues/169) Security: the configuration added by the bundle to the `security` extension (`process_user_provider` user provider, provider, login form, logout and API token authenticator of the `main` firewall) overrides the application one, it can now be disabled with the new `security.configure_firewall` option (default `true`, same behaviour as before; `false`: no `security` configuration is added, the application configures the provider and the firewall, e.g. a firewall dedicated to the UI). The `monolog`, `doctrine_migrations` and `security` configurations are skipped when the bundle is not enabled, instead of a `LogicException`. The documentation no longer says that these configurations are prepended (only the Messenger one is). Update documentation, add tests.

Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"easycorp/easyadmin-bundle": "^5",
"monolog/monolog": "^3",
"psr/log": "^2 || ^3",
"symfony/asset": "^6.4 || ^7.4 || ^8",
"symfony/config": "^6.4 || ^7.4 || ^8",
"symfony/console": "^6.4 || ^7.4 || ^8",
"symfony/dependency-injection": "^6.4 || ^7.4 || ^8",
Expand Down
5 changes: 4 additions & 1 deletion src/Controller/Admin/ProcessDashboardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
use Symfony\Component\Asset\Packages;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Translation\LocaleSwitcher;
Expand All @@ -30,6 +31,7 @@ class ProcessDashboardController extends AbstractDashboardController
{
public function __construct(
private readonly LocaleSwitcher $localeSwitcher,
private readonly Packages $packages,
private readonly string $logoPath = '',
) {
}
Expand All @@ -48,7 +50,8 @@ public function configureDashboard(): Dashboard
{
return Dashboard::new()
->renderContentMaximized()
->setTitle('<img src="/'.$this->logoPath.'" />');
// Asset URL: base path of the application, assets base URLs and version strategy; absolute URLs kept as is
->setTitle(\sprintf('<img src="%s" />', htmlspecialchars($this->packages->getUrl($this->logoPath))));
}

#[\Override]
Expand Down
2 changes: 1 addition & 1 deletion src/Controller/Admin/Security/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function __invoke(AuthenticationUtils $authenticationUtils): Response
'@CleverAgeUiProcess/admin/login.html.twig',
[
'page_title' => 'Login',
'target_path' => '/process',
'target_path' => $this->generateUrl('process'),
'error' => $authenticationUtils->getLastAuthenticationError(),
'last_username' => $authenticationUtils->getLastUsername(),
]
Expand Down
62 changes: 62 additions & 0 deletions tests/Controller/Admin/ProcessDashboardControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

/*
* This file is part of the CleverAge/UiProcessBundle package.
*
* Copyright (c) Clever-Age
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace CleverAge\UiProcessBundle\Tests\Controller\Admin;

use CleverAge\UiProcessBundle\Controller\Admin\ProcessDashboardController;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Asset\Context\RequestStackContext;
use Symfony\Component\Asset\Packages;
use Symfony\Component\Asset\PathPackage;
use Symfony\Component\Asset\UrlPackage;
use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Translation\LocaleSwitcher;

/**
* The logo URL is built by the Asset component: base path of the application, assets base URLs.
*/
#[CoversClass(ProcessDashboardController::class)]
class ProcessDashboardControllerTest extends TestCase
{
/**
* @return iterable<string, array{Packages, string, string}>
*/
public static function provideLogos(): iterable
{
$requestStack = new RequestStack();
$request = Request::create('http://localhost/app/process', server: ['SCRIPT_NAME' => '/app/index.php', 'SCRIPT_FILENAME' => '/var/www/public/index.php']);
$requestStack->push($request);
$pathPackage = new Packages(new PathPackage('/', new EmptyVersionStrategy(), new RequestStackContext($requestStack)));

yield 'default' => [$pathPackage, 'bundles/cleverageuiprocess/logo.jpg', '<img src="/app/bundles/cleverageuiprocess/logo.jpg" />'];
yield 'absolute URL' => [$pathPackage, 'https://cdn.example.com/logo.png', '<img src="https://cdn.example.com/logo.png" />'];
yield 'assets base URL' => [
new Packages(new UrlPackage('https://cdn.example.com/', new EmptyVersionStrategy())),
'bundles/cleverageuiprocess/logo.jpg',
'<img src="https://cdn.example.com/bundles/cleverageuiprocess/logo.jpg" />',
];
yield 'escaped' => [$pathPackage, 'logo.png?a=1&b="2"', '<img src="/app/logo.png?a=1&amp;b=&quot;2&quot;" />'];
}

#[DataProvider('provideLogos')]
public function testLogoUrl(Packages $packages, string $logoPath, string $expectedTitle): void
{
$controller = new ProcessDashboardController(new LocaleSwitcher('en', []), $packages, $logoPath);

self::assertSame($expectedTitle, $controller->configureDashboard()->getAsDto()->getTitle());
}
}
11 changes: 11 additions & 0 deletions tests/Functional/SecurityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ public function testLogin(): void
self::assertResponseIsSuccessful();
}

/**
* Application served from a sub-directory: the page displayed after the login is the dashboard of this application.
*/
public function testLoginTargetPathFromASubDirectory(): void
{
$crawler = $this->client->request('GET', '/app/process/login', server: ['SCRIPT_NAME' => '/app/index.php', 'SCRIPT_FILENAME' => '/var/www/public/index.php']);

self::assertResponseIsSuccessful();
self::assertSame('/app/process', $crawler->filter('input[name="_target_path"]')->attr('value'));
}

public function testLoginWithAnInvalidPassword(): void
{
$this->createUser('[email protected]', ['ROLE_ADMIN'], 'secret');
Expand Down
Loading