Skip to content

Commit 9f87d4e

Browse files
Redirect login page to SAML2 login if only auth provider (#3472)
This PR adds logic to automatically redirect the login page to the configured SAML2 provider, if SAML2 is the only configured authentication provider. This bypasses the need to click on a single button on the login form for instances where the only means of logging in is SAML.
1 parent b13f742 commit 9f87d4e

2 files changed

Lines changed: 64 additions & 17 deletions

File tree

app/Http/Controllers/Auth/LoginController.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Illuminate\Support\Facades\DB;
1111
use Illuminate\Support\Facades\Schema;
1212
use Illuminate\Validation\ValidationException;
13+
use Illuminate\View\View;
1314
use LdapRecord\Laravel\Auth\ListensForLdapBindFailure;
1415
use Symfony\Component\HttpFoundation\Response;
1516

@@ -27,6 +28,7 @@ final class LoginController extends AbstractController
2728
*/
2829

2930
use AuthenticatesUsers {
31+
showLoginForm as traitShowLoginForm;
3032
login as traitLogin;
3133
credentials as traitCredentials;
3234
}
@@ -47,6 +49,22 @@ public function __construct()
4749
$this->listenForLdapBindFailure();
4850
}
4951

52+
public function showLoginForm(): View|Response|RedirectResponse
53+
{
54+
$hasOAuthLogin = collect(config('services'))->where('oauth', 'true')->firstWhere('enable', true) !== null;
55+
$hasSAMLLogin = config('saml2.enabled') === true;
56+
57+
if (
58+
config('cdash.username_password_authentication_enabled') === false
59+
&& !$hasOAuthLogin
60+
&& $hasSAMLLogin
61+
) {
62+
return self::saml2Login();
63+
}
64+
65+
return $this->traitShowLoginForm();
66+
}
67+
5068
/**
5169
* Handle a login request to the application.
5270
*

tests/Feature/LoginAndRegistration.php

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,30 @@ public function testLocalView(): void
143143
unlink($tmp_view_path);
144144
}
145145

146+
/** @return array<string, string|int> */
147+
private function createSamlTenant(): array
148+
{
149+
$saml_uuid = (string) Str::uuid();
150+
$saml2_tenant_name = 'saml2_client_for_testing';
151+
$saml2_tenant_uri = 'https://cdash.org/fake-saml2-idp/asdf';
152+
$params = [
153+
'uuid' => $saml_uuid,
154+
'key' => $saml2_tenant_name,
155+
'idp_entity_id' => $saml2_tenant_uri,
156+
'idp_login_url' => "$saml2_tenant_uri/login",
157+
'idp_logout_url' => "$saml2_tenant_uri/logout",
158+
'idp_x509_cert' => base64_encode('asdf'),
159+
'metadata' => '{}',
160+
'name_id_format' => 'persistent',
161+
];
162+
$saml2_tenant_id = DB::table('saml2_tenants')->insertGetId($params);
163+
164+
return [
165+
...$params,
166+
'saml2_tenant_id' => $saml2_tenant_id,
167+
];
168+
}
169+
146170
public function testSaml2(): void
147171
{
148172
// Verify that SAML2 login fails when disabled.
@@ -169,28 +193,33 @@ public function testSaml2(): void
169193
$response->assertStatus(500);
170194
$response->assertSeeText('SAML2 tenant not found');
171195

172-
// Create a SAML2 tenant.
173-
$saml_uuid = (string) Str::uuid();
174-
$saml2_tenant_name = 'saml2_client_for_testing';
175-
$saml2_tenant_uri = 'https://cdash.org/fake-saml2-idp/asdf';
176-
$params = [
177-
'uuid' => $saml_uuid,
178-
'key' => $saml2_tenant_name,
179-
'idp_entity_id' => $saml2_tenant_uri,
180-
'idp_login_url' => "$saml2_tenant_uri/login",
181-
'idp_logout_url' => "$saml2_tenant_uri/logout",
182-
'idp_x509_cert' => base64_encode('asdf'),
183-
'metadata' => '{}',
184-
'name_id_format' => 'persistent',
185-
];
186-
$saml2_tenant_id = DB::table('saml2_tenants')->insertGetId($params);
196+
$saml_tenant = $this->createSamlTenant();
187197

188198
// Verify that SAML2 login redirects as expected.
189199
$response = $this->post('/saml2/login');
190-
$response->assertRedirectContains("/saml2/{$saml_uuid}/login?returnTo=");
200+
$response->assertRedirectContains("/saml2/{$saml_tenant['uuid']}/login?returnTo=");
201+
202+
// Delete SAML2 tenant.
203+
DB::table('saml2_tenants')->delete($saml_tenant['saml2_tenant_id']);
204+
}
205+
206+
public function testRedirectsToSamlIfOnlyAuthProvider(): void
207+
{
208+
config(['saml2.enabled' => true]);
209+
$saml_tenant = $this->createSamlTenant();
210+
211+
// Verify that we see the login page if username+password is enabled
212+
$response = $this->get('/login');
213+
$response->assertViewIs('auth.login');
214+
215+
config(['cdash.username_password_authentication_enabled' => false]);
216+
217+
// Verify that we get redirected to the SAML2 route if it's the only configured auth provider
218+
$response = $this->get('/login');
219+
$response->assertRedirectContains("/saml2/{$saml_tenant['uuid']}/login?returnTo=");
191220

192221
// Delete SAML2 tenant.
193-
DB::table('saml2_tenants')->delete($saml2_tenant_id);
222+
DB::table('saml2_tenants')->delete($saml_tenant['saml2_tenant_id']);
194223
}
195224

196225
/**

0 commit comments

Comments
 (0)