@@ -1476,6 +1476,112 @@ private TestsSSLService buildTestSslService() {
14761476 return new TestsSSLService (TestEnvironment .newEnvironment (settings ));
14771477 }
14781478
1479+ public void testHttpMetadataWithCustomTimeouts () throws Exception {
1480+ final Path path = getDataPath ("idp1.xml" );
1481+ final String body = Files .readString (path );
1482+ TestsSSLService sslService = buildTestSslService ();
1483+ try (MockWebServer proxyServer = new MockWebServer (sslService .sslContext ("xpack.security.http.ssl" ), false )) {
1484+ proxyServer .start ();
1485+ proxyServer .enqueue (new MockResponse ().setResponseCode (200 ).setBody (body ).addHeader ("Content-Type" , "application/xml" ));
1486+
1487+ final TimeValue customConnectTimeout = TimeValue .timeValueSeconds (3 );
1488+ final TimeValue customReadTimeout = TimeValue .timeValueSeconds (15 );
1489+
1490+ Tuple <RealmConfig , SSLService > config = buildConfig ("https://localhost:" + proxyServer .getPort (), builder -> {
1491+ builder .put (
1492+ SingleSpSamlRealmSettings .getFullSettingKey (REALM_NAME , SamlRealmSettings .IDP_METADATA_HTTP_CONNECT_TIMEOUT ),
1493+ customConnectTimeout .getStringRep ()
1494+ );
1495+ builder .put (
1496+ SingleSpSamlRealmSettings .getFullSettingKey (REALM_NAME , SamlRealmSettings .IDP_METADATA_HTTP_READ_TIMEOUT ),
1497+ customReadTimeout .getStringRep ()
1498+ );
1499+ });
1500+
1501+ // Verify settings are correctly configured
1502+ assertThat (config .v1 ().getSetting (SamlRealmSettings .IDP_METADATA_HTTP_CONNECT_TIMEOUT ), equalTo (customConnectTimeout ));
1503+ assertThat (config .v1 ().getSetting (SamlRealmSettings .IDP_METADATA_HTTP_READ_TIMEOUT ), equalTo (customReadTimeout ));
1504+
1505+ final ResourceWatcherService watcherService = mock (ResourceWatcherService .class );
1506+ Tuple <AbstractReloadingMetadataResolver , Supplier <EntityDescriptor >> tuple = SamlRealm .initializeResolver (
1507+ logger ,
1508+ config .v1 (),
1509+ config .v2 (),
1510+ watcherService
1511+ );
1512+
1513+ try {
1514+ assertThat (proxyServer .requests ().size (), greaterThanOrEqualTo (1 ));
1515+ assertIdp1MetadataParsedCorrectly (tuple .v2 ().get ());
1516+ } finally {
1517+ tuple .v1 ().destroy ();
1518+ }
1519+ }
1520+ }
1521+
1522+ public void testHttpMetadataWithDefaultTimeouts () throws Exception {
1523+ final Path path = getDataPath ("idp1.xml" );
1524+ final String body = Files .readString (path );
1525+ TestsSSLService sslService = buildTestSslService ();
1526+ try (MockWebServer proxyServer = new MockWebServer (sslService .sslContext ("xpack.security.http.ssl" ), false )) {
1527+ proxyServer .start ();
1528+ proxyServer .enqueue (new MockResponse ().setResponseCode (200 ).setBody (body ).addHeader ("Content-Type" , "application/xml" ));
1529+
1530+ Tuple <RealmConfig , SSLService > config = buildConfig ("https://localhost:" + proxyServer .getPort ());
1531+
1532+ // Verify default timeout values are used
1533+ assertThat (config .v1 ().getSetting (SamlRealmSettings .IDP_METADATA_HTTP_CONNECT_TIMEOUT ), equalTo (TimeValue .timeValueSeconds (5 )));
1534+ assertThat (config .v1 ().getSetting (SamlRealmSettings .IDP_METADATA_HTTP_READ_TIMEOUT ), equalTo (TimeValue .timeValueSeconds (10 )));
1535+
1536+ final ResourceWatcherService watcherService = mock (ResourceWatcherService .class );
1537+ Tuple <AbstractReloadingMetadataResolver , Supplier <EntityDescriptor >> tuple = SamlRealm .initializeResolver (
1538+ logger ,
1539+ config .v1 (),
1540+ config .v2 (),
1541+ watcherService
1542+ );
1543+
1544+ try {
1545+ assertThat (proxyServer .requests ().size (), greaterThanOrEqualTo (1 ));
1546+ assertIdp1MetadataParsedCorrectly (tuple .v2 ().get ());
1547+ } finally {
1548+ tuple .v1 ().destroy ();
1549+ }
1550+ }
1551+ }
1552+
1553+ public void testHttpMetadataConnectionTimeout () throws Exception {
1554+ // Use a non-routable IP address to simulate connection timeout
1555+ // 192.0.2.1 is reserved for documentation and will not be routable
1556+ final String unreachableUrl = "https://192.0.2.1:9999/metadata.xml" ;
1557+ final TimeValue shortConnectTimeout = TimeValue .timeValueMillis (100 );
1558+
1559+ Tuple <RealmConfig , SSLService > config = buildConfig (unreachableUrl , builder -> {
1560+ builder .put (
1561+ SingleSpSamlRealmSettings .getFullSettingKey (REALM_NAME , SamlRealmSettings .IDP_METADATA_HTTP_CONNECT_TIMEOUT ),
1562+ shortConnectTimeout .getStringRep ()
1563+ );
1564+ builder .put (SingleSpSamlRealmSettings .getFullSettingKey (REALM_NAME , SamlRealmSettings .IDP_METADATA_HTTP_FAIL_ON_ERROR ), false );
1565+ });
1566+
1567+ final ResourceWatcherService watcherService = mock (ResourceWatcherService .class );
1568+
1569+ // initialization should complete even though the connection fails
1570+ Tuple <AbstractReloadingMetadataResolver , Supplier <EntityDescriptor >> tuple = SamlRealm .initializeResolver (
1571+ logger ,
1572+ config .v1 (),
1573+ config .v2 (),
1574+ watcherService
1575+ );
1576+
1577+ try {
1578+ EntityDescriptor descriptor = tuple .v2 ().get ();
1579+ assertThat (descriptor , instanceOf (UnresolvedEntity .class ));
1580+ } finally {
1581+ tuple .v1 ().destroy ();
1582+ }
1583+ }
1584+
14791585 private void assertIdp1MetadataParsedCorrectly (EntityDescriptor descriptor ) {
14801586 try {
14811587 IDPSSODescriptor idpssoDescriptor = descriptor .getIDPSSODescriptor (SAMLConstants .SAML20P_NS );
0 commit comments