From f7996e31a4e1e6d4e1a64539052d63142b53c300 Mon Sep 17 00:00:00 2001 From: Teisha McRae Date: Tue, 2 Jun 2026 12:36:48 -0400 Subject: [PATCH] Honor Dynamic UI home page on redirect When no intended URL cookie is present, HomeController now checks for a tenant-configured DynamicUI home page and redirects there if available. This preserves per-tenant landing pages while still falling back to the existing requests/home routes. Tests added: INTENDED_DEEP_LINK constant; ensure redirect-to-intended honors the cookie; ensure the intended cookie takes precedence over a DynamicUI home (regression for SAML flow); verify the intended cookie is cleared after use; and verify fallback to route('home') when no cookie and no dashboard is configured. --- .../Http/Controllers/HomeController.php | 10 +++ tests/Feature/HomeControllerTest.php | 88 +++++++++++++++++-- 2 files changed, 93 insertions(+), 5 deletions(-) diff --git a/ProcessMaker/Http/Controllers/HomeController.php b/ProcessMaker/Http/Controllers/HomeController.php index 201d8c6775..3b211b203c 100644 --- a/ProcessMaker/Http/Controllers/HomeController.php +++ b/ProcessMaker/Http/Controllers/HomeController.php @@ -60,6 +60,16 @@ public function redirectToIntended() return redirect($url)->withCookie(\Cookie::forget('processmaker_intended')); } + // No intended URL. Honor the tenant's package-dynamic-ui home page + // if it's installed, so admins'configured landing pages are still + // respected, before falling back to /requests. + if (Auth::check() && class_exists(\ProcessMaker\Package\PackageDynamicUI\Models\DynamicUI::class)) { + $homePage = \ProcessMaker\Package\PackageDynamicUI\Models\DynamicUI::getHomePage(Auth::user()); + if (!empty($homePage)) { + return redirect($homePage); + } + } + return redirect()->route('requests.index'); } } diff --git a/tests/Feature/HomeControllerTest.php b/tests/Feature/HomeControllerTest.php index ebd1527464..32e8abbb95 100644 --- a/tests/Feature/HomeControllerTest.php +++ b/tests/Feature/HomeControllerTest.php @@ -2,16 +2,22 @@ namespace Tests\Feature; -use Tests\TestCase; -use ProcessMaker\Models\User; use ProcessMaker\Models\Group; +use ProcessMaker\Models\User; use ProcessMaker\Package\PackageDynamicUI\Models\DynamicUI; use Tests\Feature\Shared\RequestHelper; +use Tests\TestCase; class HomeControllerTest extends TestCase { use RequestHelper; + /** + * Sample deep-link URL used as the "originally requested" page for the + * redirect-to-intended tests. + */ + private const INTENDED_DEEP_LINK = '/designer/scripts/123/edit'; + protected function setUp(): void { parent::setUp(); @@ -32,7 +38,7 @@ public function testRedirectsToLoginWhenNotAuthenticated() public function testRedirectsToCustomDashboardWhenUserHasDashboard() { $user = User::factory()->create(); - + // Create a custom dashboard for the user DynamicUI::create([ 'type' => 'DASHBOARD', @@ -68,7 +74,7 @@ public function testRedirectsToCustomDashboardWhenGroupHasDashboard() public function testRedirectsToTasksOnMobileWithoutCustomDashboard() { $user = User::factory()->create(); - + // Mock MobileHelper to return true $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1'; $_COOKIE['isMobile'] = 'true'; @@ -84,7 +90,7 @@ public function testRedirectsToTasksOnMobileWithoutCustomDashboard() public function testRedirectsToInboxOnDesktopWithoutCustomDashboard() { $user = User::factory()->create(); - + // Mock MobileHelper to return false $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36'; $_COOKIE['isMobile'] = 'false'; @@ -131,4 +137,76 @@ public function testRedirectsToGroupUrlRedirect() $response = $this->actingAs($user)->get('/'); $response->assertRedirect('https://processmaker.com/home'); } + + /** @test */ + public function testRedirectToIntendedHonorsCookie() + { + $user = User::factory()->create(); + $response = $this->actingAs($user) + ->withCookie('processmaker_intended', self::INTENDED_DEEP_LINK) + ->get(route('redirect_to_intended')); + $response->assertRedirect(self::INTENDED_DEEP_LINK); + } + + /** + * Regression test for the SAML "land on home instead of intended URL" + * bug: when the user comes back from the SSO callback through the + * /redirect-to-intended recovery hop with a valid `processmaker_intended` + * cookie, the cookie wins -- the configured Dynamic UI home page does + * not preempt it. + * + * @test + */ + public function testRedirectToIntendedCookieTakesPrecedenceOverDynamicUiHome() + { + $user = User::factory()->create(); + + DynamicUI::create([ + 'type' => 'URL', + 'assignable_id' => $user->id, + 'assignable_type' => User::class, + 'homepage' => 'https://processmaker.com/configured-landing', + ]); + + $response = $this->actingAs($user) + ->withCookie('processmaker_intended', self::INTENDED_DEEP_LINK) + ->get(route('redirect_to_intended')); + + $response->assertRedirect(self::INTENDED_DEEP_LINK); + } + + /** + * The intended-URL cookie is single-use: once we've consumed it we must + * tell the browser to drop it, otherwise it would keep deflecting every + * subsequent SSO callback to the same stale URL. + * + * @test + */ + public function testRedirectToIntendedClearsCookieAfterUse() + { + $user = User::factory()->create(); + + $response = $this->actingAs($user) + ->withCookie('processmaker_intended', self::INTENDED_DEEP_LINK) + ->get(route('redirect_to_intended')); + + $response->assertRedirect(self::INTENDED_DEEP_LINK); + $response->assertCookieExpired('processmaker_intended'); + } + + /** + * When there's no intended URL at all we still need to send the user somewhere. + * With no dashboard configured, DynamicUI::getHomePage() falls through to route('home') + * which HomeController::index() will then re-route to /inbox (or /tasks on mobile) on the next request. + * + * @test + */ + public function testRedirectToIntendedFallsBackToHomeWhenNoCookieAndNoDashboard() + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->get(route('redirect_to_intended')); + + $response->assertRedirect(route('home')); + } }