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