|
17 | 17 | import edu.harvard.iq.dataverse.authorization.providers.oauth2.OAuth2AuthenticationProviderFactory; |
18 | 18 | import edu.harvard.iq.dataverse.authorization.providers.oauth2.impl.OrcidOAuth2AP; |
19 | 19 | import edu.harvard.iq.dataverse.authorization.providers.oauth2.oidc.OIDCAuthenticationProviderFactory; |
| 20 | +import edu.harvard.iq.dataverse.authorization.providers.shib.ShibAuthenticationProvider; |
20 | 21 | import edu.harvard.iq.dataverse.authorization.providers.shib.ShibAuthenticationProviderFactory; |
| 22 | +import edu.harvard.iq.dataverse.settings.FeatureFlags; |
21 | 23 | import edu.harvard.iq.dataverse.settings.JvmSettings; |
| 24 | +import edu.harvard.iq.dataverse.settings.SettingsServiceBean; |
| 25 | +import edu.harvard.iq.dataverse.util.SystemConfig; |
22 | 26 | import edu.harvard.iq.dataverse.validation.PasswordValidatorServiceBean; |
23 | 27 | import java.util.HashMap; |
24 | 28 | import java.util.Map; |
|
33 | 37 | import jakarta.inject.Named; |
34 | 38 | import jakarta.persistence.EntityManager; |
35 | 39 | import jakarta.persistence.PersistenceContext; |
| 40 | +import java.io.IOException; |
| 41 | +import java.io.InputStream; |
| 42 | +import java.io.InputStreamReader; |
| 43 | +import java.net.HttpURLConnection; |
| 44 | +import java.net.MalformedURLException; |
| 45 | +import java.net.URL; |
| 46 | +import javax.xml.stream.XMLInputFactory; |
| 47 | +import javax.xml.stream.XMLStreamConstants; |
| 48 | +import javax.xml.stream.XMLStreamException; |
| 49 | +import javax.xml.stream.XMLStreamReader; |
36 | 50 |
|
37 | 51 | /** |
38 | 52 | * |
@@ -63,6 +77,9 @@ public class AuthenticationProvidersRegistrationServiceBean { |
63 | 77 | @EJB |
64 | 78 | AuthenticationServiceBean authenticationService; |
65 | 79 |
|
| 80 | + @EJB |
| 81 | + SettingsServiceBean settingsService; |
| 82 | + |
66 | 83 | /** |
67 | 84 | * The maps below (the objects themselves) are "final", but the |
68 | 85 | * values will be populated in @PostConstruct (see below) during |
@@ -117,7 +134,32 @@ public void startup() { |
117 | 134 | .getResultList().forEach((row) -> { |
118 | 135 | if(row.isEnabled()) { |
119 | 136 | try { |
120 | | - registerProvider( loadProvider(row) ); |
| 137 | + AuthenticationProvider authProvider = loadProvider(row); |
| 138 | + |
| 139 | + registerProvider( authProvider ); |
| 140 | + |
| 141 | + // For production Shibboleth instances that are not using |
| 142 | + // the legacy DiscoFeed-based workflow, we need to call |
| 143 | + // shibd to look up and cache its entityID, since it will |
| 144 | + // be needed in order to issue WayFinder service redirects. |
| 145 | + |
| 146 | + if ("shib".equals(authProvider.getId()) |
| 147 | + && FeatureFlags.SHIBBOLETH_USE_WAYFINDER.enabled()) { |
| 148 | + // ... is this a prod. shibboleth instance? |
| 149 | + String shibTypeSetting = settingsService.getValueForKey(SettingsServiceBean.Key.DebugShibAccountType, null); |
| 150 | + boolean isProduction = shibTypeSetting == null || shibTypeSetting.equals("PRODUCTION"); |
| 151 | + |
| 152 | + if (isProduction) { |
| 153 | + String spEntityId = lookupShibbolethEntityId(); |
| 154 | + logger.info("Looked up the entityId of the shibboleth service provider (via a call to shibd): " |
| 155 | + + spEntityId); |
| 156 | + if (spEntityId == null) { |
| 157 | + // we'll make this educated guess - it may or may not help us later on: |
| 158 | + spEntityId = SystemConfig.getDataverseSiteUrlStatic() + "/sp"; |
| 159 | + } |
| 160 | + ((ShibAuthenticationProvider) authProvider).setServiceProviderEntityId(spEntityId); |
| 161 | + } |
| 162 | + } |
121 | 163 |
|
122 | 164 | } catch ( AuthenticationProviderFactoryNotFoundException e ) { |
123 | 165 | logger.log(Level.SEVERE, "Cannot find authentication provider factory with alias '" + e.getFactoryAlias() + "'",e); |
@@ -307,5 +349,75 @@ public boolean isOrcidEnabled() { |
307 | 349 | return oAuth2authenticationProviders.values().stream().anyMatch( s -> s.getId().toLowerCase().contains("orcid") ); |
308 | 350 | } |
309 | 351 | */ |
| 352 | + |
| 353 | + private String lookupShibbolethEntityId() { |
| 354 | + |
| 355 | + String baseUrl; |
| 356 | + if (FeatureFlags.SHIBBOLETH_USE_LOCALHOST.enabled()) { |
| 357 | + baseUrl = "http://localhost"; |
| 358 | + } else { |
| 359 | + baseUrl = SystemConfig.getDataverseSiteUrlStatic(); |
| 360 | + } |
| 361 | + |
| 362 | + String urlString = baseUrl + "/Shibboleth.sso/Metadata"; |
| 363 | + |
| 364 | + URL url = null; |
| 365 | + try { |
| 366 | + url = new URL(urlString); |
| 367 | + } catch (MalformedURLException ex) { |
| 368 | + logger.warning(ex.toString()); |
| 369 | + return null; |
| 370 | + } |
| 371 | + |
| 372 | + if (url == null) { |
| 373 | + logger.warning("url object was null after parsing " + urlString); |
| 374 | + return null; |
| 375 | + } |
| 376 | + |
| 377 | + HttpURLConnection metadataRequest = null; |
| 378 | + try { |
| 379 | + metadataRequest = (HttpURLConnection) url.openConnection(); |
| 380 | + } catch (IOException ex) { |
| 381 | + logger.warning(ex.toString()); |
| 382 | + return null; |
| 383 | + } |
| 384 | + if (metadataRequest == null) { |
| 385 | + logger.warning("http request was null for a local /Shibboleth.sso/Metadata call"); |
| 386 | + return null; |
| 387 | + } |
| 388 | + try { |
| 389 | + metadataRequest.connect(); |
| 390 | + } catch (IOException ex) { |
| 391 | + logger.warning(ex.toString()); |
| 392 | + return null; |
| 393 | + } |
| 394 | + |
| 395 | + XMLStreamReader xmlr = null; |
| 396 | + |
| 397 | + try { |
| 398 | + XMLInputFactory xmlFactory = javax.xml.stream.XMLInputFactory.newInstance(); |
| 399 | + xmlr = xmlFactory.createXMLStreamReader(new InputStreamReader((InputStream) metadataRequest.getInputStream())); |
| 400 | + |
| 401 | + while ( xmlr.next() == XMLStreamConstants.COMMENT); |
| 402 | + xmlr.require(XMLStreamConstants.START_ELEMENT, null, "EntityDescriptor"); |
| 403 | + |
| 404 | + return xmlr.getAttributeValue(null, "entityID"); |
| 405 | + |
| 406 | + } catch (IOException ioex) { |
| 407 | + logger.warning("IOException instantiating a stream reader of the /Shibboleth.sso/Metadata output" + ioex.getMessage()); |
| 408 | + } catch (XMLStreamException xsex) { |
| 409 | + logger.warning("Failed to parse the xml output of the /Shibboleth.sso/Metadata; " + xsex.getMessage()); |
| 410 | + } finally { |
| 411 | + if (xmlr != null) { |
| 412 | + try { |
| 413 | + logger.fine("closing xml reader"); |
| 414 | + xmlr.close(); |
| 415 | + } catch (XMLStreamException xsex) { |
| 416 | + // we don't care |
| 417 | + } |
| 418 | + } |
| 419 | + } |
| 420 | + return null; |
| 421 | + } |
310 | 422 |
|
311 | 423 | } |
0 commit comments