From f9a44c4da45dd440f4f4ae3e42b85b6b0d81facd Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Wed, 17 Dec 2025 10:52:48 +0000 Subject: [PATCH] Saml fixes (#5256) # Description of Changes --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- Co-authored-by: ConnorYoh <40631091+ConnorYoh@users.noreply.github.com> --- .../common/configuration/AppConfig.java | 15 ++++++++--- .../software/SPDF/SPDFApplication.java | 6 ++--- .../controller/api/misc/ConfigController.java | 3 ++- .../security/CustomLogoutSuccessHandler.java | 2 +- .../configuration/SecurityConfiguration.java | 3 ++- .../security/saml2/Saml2Configuration.java | 25 +++++++++++++++---- .../UserLicenseSettingsServiceTest.java | 3 ++- 7 files changed, 41 insertions(+), 16 deletions(-) diff --git a/app/common/src/main/java/stirling/software/common/configuration/AppConfig.java b/app/common/src/main/java/stirling/software/common/configuration/AppConfig.java index 272c0b35c..1e9d67269 100644 --- a/app/common/src/main/java/stirling/software/common/configuration/AppConfig.java +++ b/app/common/src/main/java/stirling/software/common/configuration/AppConfig.java @@ -37,10 +37,6 @@ public class AppConfig { private final ApplicationProperties applicationProperties; - @Getter - @Value("${baseUrl:http://localhost}") - private String baseUrl; - @Getter @Value("${server.servlet.context-path:/}") private String contextPath; @@ -49,6 +45,17 @@ public class AppConfig { @Value("${server.port:8080}") private String serverPort; + /** + * Get the backend URL from system configuration. Falls back to http://localhost if not + * configured. + * + * @return The backend base URL for SAML/OAuth/API callbacks + */ + public String getBackendUrl() { + String backendUrl = applicationProperties.getSystem().getBackendUrl(); + return (backendUrl != null && !backendUrl.isBlank()) ? backendUrl : "http://localhost"; + } + @Value("${v2}") public boolean v2Enabled; diff --git a/app/core/src/main/java/stirling/software/SPDF/SPDFApplication.java b/app/core/src/main/java/stirling/software/SPDF/SPDFApplication.java index d3a4ce776..9cb4a786a 100644 --- a/app/core/src/main/java/stirling/software/SPDF/SPDFApplication.java +++ b/app/core/src/main/java/stirling/software/SPDF/SPDFApplication.java @@ -138,13 +138,13 @@ public class SPDFApplication { @PostConstruct public void init() { - String baseUrl = appConfig.getBaseUrl(); + String backendUrl = appConfig.getBackendUrl(); String contextPath = appConfig.getContextPath(); String serverPort = appConfig.getServerPort(); - baseUrlStatic = baseUrl; + baseUrlStatic = backendUrl; contextPathStatic = contextPath; serverPortStatic = serverPort; - String url = baseUrl + ":" + getStaticPort() + contextPath; + String url = backendUrl + ":" + getStaticPort() + contextPath; // Log Tauri mode information if (Boolean.parseBoolean(System.getProperty("STIRLING_PDF_TAURI_MODE", "false"))) { diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ConfigController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ConfigController.java index 95486ff9b..2f8d4b62b 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ConfigController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/misc/ConfigController.java @@ -66,7 +66,8 @@ public class ConfigController { AppConfig appConfig = applicationContext.getBean(AppConfig.class); // Extract key configuration values from AppConfig - configData.put("baseUrl", appConfig.getBaseUrl()); + // Note: Frontend expects "baseUrl" field name for compatibility + configData.put("baseUrl", appConfig.getBackendUrl()); configData.put("contextPath", appConfig.getContextPath()); configData.put("serverPort", appConfig.getServerPort()); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/CustomLogoutSuccessHandler.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/CustomLogoutSuccessHandler.java index 16272b37f..1f55dd25f 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/CustomLogoutSuccessHandler.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/CustomLogoutSuccessHandler.java @@ -198,7 +198,7 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { private SamlClient getSamlClient( String registrationId, SAML2 samlConf, List certificates) throws SamlException { - String serverUrl = appConfig.getBaseUrl() + ":" + appConfig.getServerPort(); + String serverUrl = appConfig.getBackendUrl() + ":" + appConfig.getServerPort(); String relyingPartyIdentifier = serverUrl + "/saml2/service-provider-metadata/" + registrationId; diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java index 1226237c8..fa936211d 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java @@ -344,7 +344,8 @@ public class SecurityConfiguration { log.error("Error configuring SAML 2 login", e); throw new RuntimeException(e); } - }); + }) + .saml2Metadata(metadata -> {}); } } else { log.debug("Login is not enabled."); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/Saml2Configuration.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/Saml2Configuration.java index 99be4b5b0..6ccffa1da 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/Saml2Configuration.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/Saml2Configuration.java @@ -102,16 +102,31 @@ public class Saml2Configuration { log.error("Failed to load SAML2 SP credentials: {}", e.getMessage(), e); throw new IllegalStateException("Failed to load SAML2 SP credentials", e); } + + // Get backend URL from configuration (for SAML endpoints) + String backendUrl = applicationProperties.getSystem().getBackendUrl(); + if (backendUrl == null || backendUrl.isBlank()) { + backendUrl = "{baseUrl}"; // Fallback to Spring's auto-resolution + log.warn( + "system.backendUrl not configured - SAML metadata will use request-based URLs. Set system.backendUrl for production use."); + } else { + log.info("Using configured backend URL for SAML: {}", backendUrl); + } + + String entityId = + backendUrl + "/saml2/service-provider-metadata/" + samlConf.getRegistrationId(); + String acsLocation = backendUrl + "/login/saml2/sso/{registrationId}"; + String sloResponseLocation = backendUrl + "/login"; + RelyingPartyRegistration rp = RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId()) .signingX509Credentials(c -> c.add(signingCredential)) - .entityId(samlConf.getIdpIssuer()) + .entityId(entityId) .singleLogoutServiceBinding(Saml2MessageBinding.POST) .singleLogoutServiceLocation(samlConf.getIdpSingleLogoutUrl()) - .singleLogoutServiceResponseLocation("{baseUrl}/login") + .singleLogoutServiceResponseLocation(sloResponseLocation) .assertionConsumerServiceBinding(Saml2MessageBinding.POST) - .assertionConsumerServiceLocation( - "{baseUrl}/login/saml2/sso/{registrationId}") + .assertionConsumerServiceLocation(acsLocation) .authnRequestsSigned(true) .assertingPartyMetadata( metadata -> @@ -127,7 +142,7 @@ public class Saml2Configuration { .singleLogoutServiceLocation( samlConf.getIdpSingleLogoutUrl()) .singleLogoutServiceResponseLocation( - "{baseUrl}/login") + sloResponseLocation) .wantAuthnRequestsSigned(true)) .build(); diff --git a/app/proprietary/src/test/java/stirling/software/proprietary/service/UserLicenseSettingsServiceTest.java b/app/proprietary/src/test/java/stirling/software/proprietary/service/UserLicenseSettingsServiceTest.java index a7f8042f6..3f0214573 100644 --- a/app/proprietary/src/test/java/stirling/software/proprietary/service/UserLicenseSettingsServiceTest.java +++ b/app/proprietary/src/test/java/stirling/software/proprietary/service/UserLicenseSettingsServiceTest.java @@ -51,7 +51,8 @@ class UserLicenseSettingsServiceTest { when(applicationProperties.getPremium()).thenReturn(premium); when(applicationProperties.getAutomaticallyGenerated()).thenReturn(automaticallyGenerated); - when(automaticallyGenerated.getIsNewServer()).thenReturn(false); // Default: not a new server + when(automaticallyGenerated.getIsNewServer()) + .thenReturn(false); // Default: not a new server when(settingsRepository.findSettings()).thenReturn(Optional.of(mockSettings)); when(userService.getTotalUsersCount()).thenReturn(80L); when(settingsRepository.save(any(UserLicenseSettings.class)))