@@ -1373,6 +1373,112 @@ private TestsSSLService buildTestSslService() {
13731373 return new TestsSSLService (TestEnvironment .newEnvironment (settings ));
13741374 }
13751375
1376+ public void testHttpMetadataWithCustomTimeouts () throws Exception {
1377+ final Path path = getDataPath ("idp1.xml" );
1378+ final String body = Files .readString (path );
1379+ TestsSSLService sslService = buildTestSslService ();
1380+ try (MockWebServer proxyServer = new MockWebServer (sslService .sslContext ("xpack.security.http.ssl" ), false )) {
1381+ proxyServer .start ();
1382+ proxyServer .enqueue (new MockResponse ().setResponseCode (200 ).setBody (body ).addHeader ("Content-Type" , "application/xml" ));
1383+
1384+ final TimeValue customConnectTimeout = TimeValue .timeValueSeconds (3 );
1385+ final TimeValue customReadTimeout = TimeValue .timeValueSeconds (15 );
1386+
1387+ Tuple <RealmConfig , SSLService > config = buildConfig ("https://localhost:" + proxyServer .getPort (), builder -> {
1388+ builder .put (
1389+ SingleSpSamlRealmSettings .getFullSettingKey (REALM_NAME , SamlRealmSettings .IDP_METADATA_HTTP_CONNECT_TIMEOUT ),
1390+ customConnectTimeout .getStringRep ()
1391+ );
1392+ builder .put (
1393+ SingleSpSamlRealmSettings .getFullSettingKey (REALM_NAME , SamlRealmSettings .IDP_METADATA_HTTP_READ_TIMEOUT ),
1394+ customReadTimeout .getStringRep ()
1395+ );
1396+ });
1397+
1398+ // Verify settings are correctly configured
1399+ assertThat (config .v1 ().getSetting (SamlRealmSettings .IDP_METADATA_HTTP_CONNECT_TIMEOUT ), equalTo (customConnectTimeout ));
1400+ assertThat (config .v1 ().getSetting (SamlRealmSettings .IDP_METADATA_HTTP_READ_TIMEOUT ), equalTo (customReadTimeout ));
1401+
1402+ final ResourceWatcherService watcherService = mock (ResourceWatcherService .class );
1403+ Tuple <AbstractReloadingMetadataResolver , Supplier <EntityDescriptor >> tuple = SamlRealm .initializeResolver (
1404+ logger ,
1405+ config .v1 (),
1406+ config .v2 (),
1407+ watcherService
1408+ );
1409+
1410+ try {
1411+ assertThat (proxyServer .requests ().size (), greaterThanOrEqualTo (1 ));
1412+ assertIdp1MetadataParsedCorrectly (tuple .v2 ().get ());
1413+ } finally {
1414+ tuple .v1 ().destroy ();
1415+ }
1416+ }
1417+ }
1418+
1419+ public void testHttpMetadataWithDefaultTimeouts () throws Exception {
1420+ final Path path = getDataPath ("idp1.xml" );
1421+ final String body = Files .readString (path );
1422+ TestsSSLService sslService = buildTestSslService ();
1423+ try (MockWebServer proxyServer = new MockWebServer (sslService .sslContext ("xpack.security.http.ssl" ), false )) {
1424+ proxyServer .start ();
1425+ proxyServer .enqueue (new MockResponse ().setResponseCode (200 ).setBody (body ).addHeader ("Content-Type" , "application/xml" ));
1426+
1427+ Tuple <RealmConfig , SSLService > config = buildConfig ("https://localhost:" + proxyServer .getPort ());
1428+
1429+ // Verify default timeout values are used
1430+ assertThat (config .v1 ().getSetting (SamlRealmSettings .IDP_METADATA_HTTP_CONNECT_TIMEOUT ), equalTo (TimeValue .timeValueSeconds (5 )));
1431+ assertThat (config .v1 ().getSetting (SamlRealmSettings .IDP_METADATA_HTTP_READ_TIMEOUT ), equalTo (TimeValue .timeValueSeconds (10 )));
1432+
1433+ final ResourceWatcherService watcherService = mock (ResourceWatcherService .class );
1434+ Tuple <AbstractReloadingMetadataResolver , Supplier <EntityDescriptor >> tuple = SamlRealm .initializeResolver (
1435+ logger ,
1436+ config .v1 (),
1437+ config .v2 (),
1438+ watcherService
1439+ );
1440+
1441+ try {
1442+ assertThat (proxyServer .requests ().size (), greaterThanOrEqualTo (1 ));
1443+ assertIdp1MetadataParsedCorrectly (tuple .v2 ().get ());
1444+ } finally {
1445+ tuple .v1 ().destroy ();
1446+ }
1447+ }
1448+ }
1449+
1450+ public void testHttpMetadataConnectionTimeout () throws Exception {
1451+ // Use a non-routable IP address to simulate connection timeout
1452+ // 192.0.2.1 is reserved for documentation and will not be routable
1453+ final String unreachableUrl = "https://192.0.2.1:9999/metadata.xml" ;
1454+ final TimeValue shortConnectTimeout = TimeValue .timeValueMillis (100 );
1455+
1456+ Tuple <RealmConfig , SSLService > config = buildConfig (unreachableUrl , builder -> {
1457+ builder .put (
1458+ SingleSpSamlRealmSettings .getFullSettingKey (REALM_NAME , SamlRealmSettings .IDP_METADATA_HTTP_CONNECT_TIMEOUT ),
1459+ shortConnectTimeout .getStringRep ()
1460+ );
1461+ builder .put (SingleSpSamlRealmSettings .getFullSettingKey (REALM_NAME , SamlRealmSettings .IDP_METADATA_HTTP_FAIL_ON_ERROR ), false );
1462+ });
1463+
1464+ final ResourceWatcherService watcherService = mock (ResourceWatcherService .class );
1465+
1466+ // initialization should complete even though the connection fails
1467+ Tuple <AbstractReloadingMetadataResolver , Supplier <EntityDescriptor >> tuple = SamlRealm .initializeResolver (
1468+ logger ,
1469+ config .v1 (),
1470+ config .v2 (),
1471+ watcherService
1472+ );
1473+
1474+ try {
1475+ EntityDescriptor descriptor = tuple .v2 ().get ();
1476+ assertThat (descriptor , instanceOf (UnresolvedEntity .class ));
1477+ } finally {
1478+ tuple .v1 ().destroy ();
1479+ }
1480+ }
1481+
13761482 private void assertIdp1MetadataParsedCorrectly (EntityDescriptor descriptor ) {
13771483 try {
13781484 IDPSSODescriptor idpssoDescriptor = descriptor .getIDPSSODescriptor (SAMLConstants .SAML20P_NS );
0 commit comments