diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d5bb5e..54ea5e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 `/`). 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. diff --git a/composer.json b/composer.json index f9d2f91..8ea6225 100644 --- a/composer.json +++ b/composer.json @@ -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", diff --git a/src/Controller/Admin/ProcessDashboardController.php b/src/Controller/Admin/ProcessDashboardController.php index 9315a41..e9dcc70 100644 --- a/src/Controller/Admin/ProcessDashboardController.php +++ b/src/Controller/Admin/ProcessDashboardController.php @@ -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; @@ -30,6 +31,7 @@ class ProcessDashboardController extends AbstractDashboardController { public function __construct( private readonly LocaleSwitcher $localeSwitcher, + private readonly Packages $packages, private readonly string $logoPath = '', ) { } @@ -48,7 +50,8 @@ public function configureDashboard(): Dashboard { return Dashboard::new() ->renderContentMaximized() - ->setTitle(''); + // Asset URL: base path of the application, assets base URLs and version strategy; absolute URLs kept as is + ->setTitle(\sprintf('', htmlspecialchars($this->packages->getUrl($this->logoPath)))); } #[\Override] diff --git a/src/Controller/Admin/Security/LoginController.php b/src/Controller/Admin/Security/LoginController.php index b6ee314..e33b149 100644 --- a/src/Controller/Admin/Security/LoginController.php +++ b/src/Controller/Admin/Security/LoginController.php @@ -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(), ] diff --git a/tests/Controller/Admin/ProcessDashboardControllerTest.php b/tests/Controller/Admin/ProcessDashboardControllerTest.php new file mode 100644 index 0000000..4e89f4f --- /dev/null +++ b/tests/Controller/Admin/ProcessDashboardControllerTest.php @@ -0,0 +1,62 @@ + + */ + 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', '']; + yield 'absolute URL' => [$pathPackage, 'https://cdn.example.com/logo.png', '']; + yield 'assets base URL' => [ + new Packages(new UrlPackage('https://cdn.example.com/', new EmptyVersionStrategy())), + 'bundles/cleverageuiprocess/logo.jpg', + '', + ]; + yield 'escaped' => [$pathPackage, 'logo.png?a=1&b="2"', '']; + } + + #[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()); + } +} diff --git a/tests/Functional/SecurityTest.php b/tests/Functional/SecurityTest.php index e66ee06..58db9bb 100644 --- a/tests/Functional/SecurityTest.php +++ b/tests/Functional/SecurityTest.php @@ -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('admin@example.com', ['ROLE_ADMIN'], 'secret');