From 6c7df6fc955812599a49937f98a6746d05b0badf Mon Sep 17 00:00:00 2001 From: YuriyZ Date: Tue, 27 Dec 2022 14:32:14 +0200 Subject: [PATCH] feat(jans-auth-server): added externalUriWhiteList configuration property before call external uri from AS #3130 (#3425) --- jans-auth-server/agama/engine/pom.xml | 8 +++ jans-auth-server/agama/model/pom.xml | 13 +++++ .../model/configuration/AppConfiguration.java | 12 +++++ .../register/ws/rs/RegisterValidator.java | 14 +++-- .../ws/rs/SsaValidationConfigService.java | 11 ++-- .../as/server/service/net/UriService.java | 48 +++++++++++++++++ .../as/server/service/net/UriServiceTest.java | 53 +++++++++++++++++++ .../server/src/test/resources/testng.xml | 1 + 8 files changed, 152 insertions(+), 8 deletions(-) create mode 100644 jans-auth-server/server/src/main/java/io/jans/as/server/service/net/UriService.java create mode 100644 jans-auth-server/server/src/test/java/io/jans/as/server/service/net/UriServiceTest.java diff --git a/jans-auth-server/agama/engine/pom.xml b/jans-auth-server/agama/engine/pom.xml index 65aa9517d85..64a14a5013f 100644 --- a/jans-auth-server/agama/engine/pom.xml +++ b/jans-auth-server/agama/engine/pom.xml @@ -50,6 +50,14 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + diff --git a/jans-auth-server/agama/model/pom.xml b/jans-auth-server/agama/model/pom.xml index 69c7e9f4f2e..76c114cfac1 100644 --- a/jans-auth-server/agama/model/pom.xml +++ b/jans-auth-server/agama/model/pom.xml @@ -38,4 +38,17 @@ + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + \ No newline at end of file diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java index 82580622ec5..8ba2ea48cfc 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java @@ -570,6 +570,9 @@ public class AppConfiguration implements Configuration { @DocProperty(description = "JMS Password") private String jmsPassword; + @DocProperty(description = "This list specifies which external URIs can be called by AS (if empty any URI can be called)") + private List externalUriWhiteList; + @DocProperty(description = "This list specifies which client redirection URIs are white-listed") private List clientWhiteList; @@ -2473,6 +2476,15 @@ public void setJmsPassword(String jmsPassword) { this.jmsPassword = jmsPassword; } + public List getExternalUriWhiteList() { + if (externalUriWhiteList == null) externalUriWhiteList = new ArrayList<>(); + return externalUriWhiteList; + } + + public void setExternalUriWhiteList(List externalUriWhiteList) { + this.externalUriWhiteList = externalUriWhiteList; + } + public List getClientWhiteList() { return clientWhiteList; } diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterValidator.java b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterValidator.java index e6064a4493f..dacd65bcc6d 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterValidator.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/RegisterValidator.java @@ -20,7 +20,6 @@ import io.jans.as.model.jwt.Jwt; import io.jans.as.model.register.RegisterErrorResponseType; import io.jans.as.model.ssa.SsaValidationType; -import io.jans.as.model.util.JwtUtil; import io.jans.as.model.util.Pair; import io.jans.as.server.ciba.CIBARegisterParamsValidatorService; import io.jans.as.server.model.common.AbstractToken; @@ -28,6 +27,7 @@ import io.jans.as.server.model.common.AuthorizationGrantList; import io.jans.as.server.model.registration.RegisterParamsValidator; import io.jans.as.server.service.external.ExternalDynamicClientRegistrationService; +import io.jans.as.server.service.net.UriService; import jakarta.ejb.Stateless; import jakarta.inject.Inject; import jakarta.inject.Named; @@ -78,6 +78,9 @@ public class RegisterValidator { @Inject private SsaValidationConfigService ssaValidationConfigService; + @Inject + private UriService uriService; + public void validateNotBlank(String input, String errorReason) { if (StringUtils.isBlank(input)) { log.trace("Failed to perform client action, reason: {}", errorReason); @@ -191,7 +194,7 @@ private String getJwksString(JSONObject softwareStatement) { @Nullable private JSONObject getJwks(HttpServletRequest httpRequest, Jwt jwt, String jwksUri, String jwksStr) { if (StringUtils.isNotBlank(jwksUri)) { - return JwtUtil.getJSONWebKeys(jwksUri); + return uriService.loadJson(jwksUri); } if (StringUtils.isNotBlank(jwksStr)) { @@ -259,8 +262,11 @@ public JSONObject validateSoftwareStatement(HttpServletRequest httpServletReques } JSONObject jwks = Strings.isNullOrEmpty(jwksUriClaim) ? - new JSONObject(jwksClaim) : - JwtUtil.getJSONWebKeys(jwksUriClaim); + null : + uriService.loadJson(jwksUriClaim); + if (jwks == null && StringUtils.isNotBlank(jwksClaim)) { + jwks = new JSONObject(jwksClaim); + } boolean validSignature = cryptoProvider.verifySignature(softwareStatement.getSigningInput(), softwareStatement.getEncodedSignature(), diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/SsaValidationConfigService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/SsaValidationConfigService.java index f3fdb9abc06..15e2ff5d0f7 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/SsaValidationConfigService.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/register/ws/rs/SsaValidationConfigService.java @@ -12,7 +12,7 @@ import io.jans.as.model.register.RegisterRequestParam; import io.jans.as.model.ssa.SsaValidationConfig; import io.jans.as.model.ssa.SsaValidationType; -import io.jans.as.model.util.JwtUtil; +import io.jans.as.server.service.net.UriService; import jakarta.ejb.Stateless; import jakarta.inject.Inject; import jakarta.inject.Named; @@ -42,6 +42,9 @@ public class SsaValidationConfigService { @Inject private AbstractCryptoProvider cryptoProvider; + @Inject + private UriService uriService; + public List getByIssuer(String issuer, SsaValidationType type) { if (StringUtils.isBlank(issuer)) { return new ArrayList<>(); @@ -130,7 +133,7 @@ private boolean isSignatureValid(Jwt jwt, SsaValidationConfig config) { private JSONObject loadJwks(SsaValidationConfig config) { JSONObject jwks = null; if (StringUtils.isNotBlank(config.getJwksUri())) { - jwks = JwtUtil.getJSONWebKeys(config.getJwksUri()); + jwks = uriService.loadJson(config.getJwksUri()); } if (jwks == null && StringUtils.isNotBlank(config.getJwks())) { @@ -138,10 +141,10 @@ private JSONObject loadJwks(SsaValidationConfig config) { } if (jwks == null && StringUtils.isNotBlank(config.getConfigurationEndpoint()) && StringUtils.isNotBlank(config.getConfigurationEndpointClaim())) { - final JSONObject responseJson = JwtUtil.getJSONWebKeys(config.getConfigurationEndpoint()); + final JSONObject responseJson = uriService.loadJson(config.getConfigurationEndpoint()); final String jwksEndpoint = responseJson.optString(config.getConfigurationEndpointClaim()); if (StringUtils.isNotBlank(jwksEndpoint)) { - jwks = JwtUtil.getJSONWebKeys(jwksEndpoint); + jwks = uriService.loadJson(jwksEndpoint); } } return jwks; diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/net/UriService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/net/UriService.java new file mode 100644 index 00000000000..d00ec92bc34 --- /dev/null +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/net/UriService.java @@ -0,0 +1,48 @@ +package io.jans.as.server.service.net; + +import io.jans.as.model.configuration.AppConfiguration; +import io.jans.as.model.util.JwtUtil; +import io.jans.as.model.util.URLPatternList; +import jakarta.ejb.Stateless; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import org.apache.tika.utils.StringUtils; +import org.json.JSONObject; +import org.slf4j.Logger; + +import java.util.List; + +/** + * @author Yuriy Z + */ +@Stateless +@Named +public class UriService { + + @Inject + private Logger log; + + @Inject + private AppConfiguration appConfiguration; + + public boolean canCall(String uri) { + if (StringUtils.isBlank(uri)) { + return false; + } + + final List externalUriWhiteList = appConfiguration.getExternalUriWhiteList(); + if (externalUriWhiteList == null || externalUriWhiteList.isEmpty()) { + return true; + } + + return new URLPatternList(externalUriWhiteList).isUrlListed(uri); + } + + public JSONObject loadJson(String uri) { + if (!canCall(uri)) { + log.debug("Unable to call external uri: {}, externalUriWhiteList: {}", uri, appConfiguration.getExternalUriWhiteList()); + return null; + } + return JwtUtil.getJSONWebKeys(uri); + } +} diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/service/net/UriServiceTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/service/net/UriServiceTest.java new file mode 100644 index 00000000000..bc91feb0f06 --- /dev/null +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/service/net/UriServiceTest.java @@ -0,0 +1,53 @@ +package io.jans.as.server.service.net; + +import io.jans.as.model.configuration.AppConfiguration; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.slf4j.Logger; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Collections; + +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertFalse; + +/** + * @author Yuriy Z + */ +@Listeners(MockitoTestNGListener.class) +public class UriServiceTest { + + @InjectMocks + private UriService uriService; + + @Mock + private Logger log; + + @Mock + private AppConfiguration appConfiguration; + + @Test + public void canCall_whenExternalUriWhiteListIsBlank_shouldReturnTrue() { + when(appConfiguration.getExternalUriWhiteList()).thenReturn(new ArrayList<>()); + + assertTrue(uriService.canCall("http://example.com")); + } + + @Test + public void canCall_whenUriAllowedByExternalUriWhiteList_shouldReturnTrue() { + when(appConfiguration.getExternalUriWhiteList()).thenReturn(Collections.singletonList("example.com")); + + assertTrue(uriService.canCall("http://example.com")); + } + + @Test + public void canCall_whenUriNotAllowedByExternalUriWhiteList_shouldReturnFalse() { + when(appConfiguration.getExternalUriWhiteList()).thenReturn(Collections.singletonList("my.com")); + + assertFalse(uriService.canCall("http://example.com")); + } +} diff --git a/jans-auth-server/server/src/test/resources/testng.xml b/jans-auth-server/server/src/test/resources/testng.xml index fde8aa49b18..dbaf66c846c 100644 --- a/jans-auth-server/server/src/test/resources/testng.xml +++ b/jans-auth-server/server/src/test/resources/testng.xml @@ -18,6 +18,7 @@ +