@@ -1576,6 +1576,112 @@ private TestsSSLService buildTestSslService() {
15761576 return new TestsSSLService (TestEnvironment .newEnvironment (settings ));
15771577 }
15781578
1579+ public void testHttpMetadataWithCustomTimeouts () throws Exception {
1580+ final Path path = getDataPath ("idp1.xml" );
1581+ final String body = Files .readString (path );
1582+ TestsSSLService sslService = buildTestSslService ();
1583+ try (MockWebServer proxyServer = new MockWebServer (sslService .sslContext ("xpack.security.http.ssl" ), false )) {
1584+ proxyServer .start ();
1585+ proxyServer .enqueue (new MockResponse ().setResponseCode (200 ).setBody (body ).addHeader ("Content-Type" , "application/xml" ));
1586+
1587+ final TimeValue customConnectTimeout = TimeValue .timeValueSeconds (3 );
1588+ final TimeValue customReadTimeout = TimeValue .timeValueSeconds (15 );
1589+
1590+ Tuple <RealmConfig , SSLService > config = buildConfig ("https://localhost:" + proxyServer .getPort (), builder -> {
1591+ builder .put (
1592+ SingleSpSamlRealmSettings .getFullSettingKey (REALM_NAME , SamlRealmSettings .IDP_METADATA_HTTP_CONNECT_TIMEOUT ),
1593+ customConnectTimeout .getStringRep ()
1594+ );
1595+ builder .put (
1596+ SingleSpSamlRealmSettings .getFullSettingKey (REALM_NAME , SamlRealmSettings .IDP_METADATA_HTTP_READ_TIMEOUT ),
1597+ customReadTimeout .getStringRep ()
1598+ );
1599+ });
1600+
1601+ // Verify settings are correctly configured
1602+ assertThat (config .v1 ().getSetting (SamlRealmSettings .IDP_METADATA_HTTP_CONNECT_TIMEOUT ), equalTo (customConnectTimeout ));
1603+ assertThat (config .v1 ().getSetting (SamlRealmSettings .IDP_METADATA_HTTP_READ_TIMEOUT ), equalTo (customReadTimeout ));
1604+
1605+ final ResourceWatcherService watcherService = mock (ResourceWatcherService .class );
1606+ Tuple <AbstractReloadingMetadataResolver , Supplier <EntityDescriptor >> tuple = SamlRealm .initializeResolver (
1607+ logger ,
1608+ config .v1 (),
1609+ config .v2 (),
1610+ watcherService
1611+ );
1612+
1613+ try {
1614+ assertThat (proxyServer .requests ().size (), greaterThanOrEqualTo (1 ));
1615+ assertIdp1MetadataParsedCorrectly (tuple .v2 ().get ());
1616+ } finally {
1617+ tuple .v1 ().destroy ();
1618+ }
1619+ }
1620+ }
1621+
1622+ public void testHttpMetadataWithDefaultTimeouts () throws Exception {
1623+ final Path path = getDataPath ("idp1.xml" );
1624+ final String body = Files .readString (path );
1625+ TestsSSLService sslService = buildTestSslService ();
1626+ try (MockWebServer proxyServer = new MockWebServer (sslService .sslContext ("xpack.security.http.ssl" ), false )) {
1627+ proxyServer .start ();
1628+ proxyServer .enqueue (new MockResponse ().setResponseCode (200 ).setBody (body ).addHeader ("Content-Type" , "application/xml" ));
1629+
1630+ Tuple <RealmConfig , SSLService > config = buildConfig ("https://localhost:" + proxyServer .getPort ());
1631+
1632+ // Verify default timeout values are used
1633+ assertThat (config .v1 ().getSetting (SamlRealmSettings .IDP_METADATA_HTTP_CONNECT_TIMEOUT ), equalTo (TimeValue .timeValueSeconds (5 )));
1634+ assertThat (config .v1 ().getSetting (SamlRealmSettings .IDP_METADATA_HTTP_READ_TIMEOUT ), equalTo (TimeValue .timeValueSeconds (10 )));
1635+
1636+ final ResourceWatcherService watcherService = mock (ResourceWatcherService .class );
1637+ Tuple <AbstractReloadingMetadataResolver , Supplier <EntityDescriptor >> tuple = SamlRealm .initializeResolver (
1638+ logger ,
1639+ config .v1 (),
1640+ config .v2 (),
1641+ watcherService
1642+ );
1643+
1644+ try {
1645+ assertThat (proxyServer .requests ().size (), greaterThanOrEqualTo (1 ));
1646+ assertIdp1MetadataParsedCorrectly (tuple .v2 ().get ());
1647+ } finally {
1648+ tuple .v1 ().destroy ();
1649+ }
1650+ }
1651+ }
1652+
1653+ public void testHttpMetadataConnectionTimeout () throws Exception {
1654+ // Use a non-routable IP address to simulate connection timeout
1655+ // 192.0.2.1 is reserved for documentation and will not be routable
1656+ final String unreachableUrl = "https://192.0.2.1:9999/metadata.xml" ;
1657+ final TimeValue shortConnectTimeout = TimeValue .timeValueMillis (100 );
1658+
1659+ Tuple <RealmConfig , SSLService > config = buildConfig (unreachableUrl , builder -> {
1660+ builder .put (
1661+ SingleSpSamlRealmSettings .getFullSettingKey (REALM_NAME , SamlRealmSettings .IDP_METADATA_HTTP_CONNECT_TIMEOUT ),
1662+ shortConnectTimeout .getStringRep ()
1663+ );
1664+ builder .put (SingleSpSamlRealmSettings .getFullSettingKey (REALM_NAME , SamlRealmSettings .IDP_METADATA_HTTP_FAIL_ON_ERROR ), false );
1665+ });
1666+
1667+ final ResourceWatcherService watcherService = mock (ResourceWatcherService .class );
1668+
1669+ // initialization should complete even though the connection fails
1670+ Tuple <AbstractReloadingMetadataResolver , Supplier <EntityDescriptor >> tuple = SamlRealm .initializeResolver (
1671+ logger ,
1672+ config .v1 (),
1673+ config .v2 (),
1674+ watcherService
1675+ );
1676+
1677+ try {
1678+ EntityDescriptor descriptor = tuple .v2 ().get ();
1679+ assertThat (descriptor , instanceOf (UnresolvedEntity .class ));
1680+ } finally {
1681+ tuple .v1 ().destroy ();
1682+ }
1683+ }
1684+
15791685 private void assertIdp1MetadataParsedCorrectly (EntityDescriptor descriptor ) {
15801686 try {
15811687 IDPSSODescriptor idpssoDescriptor = descriptor .getIDPSSODescriptor (SAMLConstants .SAML20P_NS );
0 commit comments