diff --git a/modules/commons/pom.xml b/modules/commons/pom.xml index 5bcded821d..d840ba4938 100644 --- a/modules/commons/pom.xml +++ b/modules/commons/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/core/pom.xml b/modules/core/pom.xml index a088dbbd44..c72e875bca 100644 --- a/modules/core/pom.xml +++ b/modules/core/pom.xml @@ -16,7 +16,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/core/src/main/java/org/apache/synapse/endpoints/HTTPEndpoint.java b/modules/core/src/main/java/org/apache/synapse/endpoints/HTTPEndpoint.java index 6ff090c09c..ab5387f98b 100644 --- a/modules/core/src/main/java/org/apache/synapse/endpoints/HTTPEndpoint.java +++ b/modules/core/src/main/java/org/apache/synapse/endpoints/HTTPEndpoint.java @@ -140,7 +140,18 @@ private String decodeString(String value) { } } - private void processUrlTemplate(MessageContext synCtx) throws VariableExpansionException { + protected void processUrlTemplate(MessageContext synCtx) throws VariableExpansionException { + + String evaluatedUri = resolveUrlTemplate(synCtx); + if (evaluatedUri != null) { + synCtx.setTo(new EndpointReference(evaluatedUri)); + if (super.getDefinition() != null) { + synCtx.setProperty(EndpointDefinition.DYNAMIC_URL_VALUE, evaluatedUri); + } + } + } + + protected String resolveUrlTemplate(MessageContext synCtx) throws VariableExpansionException { Map variables = new HashMap(); /*The properties with uri.var.* are only considered for Outbound REST Endpoints*/ @@ -162,10 +173,10 @@ private void processUrlTemplate(MessageContext synCtx) throws VariableExpansionE if (objProperty != null) { if (objProperty instanceof String) { variables.put(propertyKey.toString(), - decodeString((String) synCtx.getProperty(propertyKey.toString()))); + decodeString((String) synCtx.getProperty(propertyKey.toString()))); } else { variables.put(propertyKey.toString(), - decodeString(String.valueOf(synCtx.getProperty(propertyKey.toString())))); + decodeString(String.valueOf(synCtx.getProperty(propertyKey.toString())))); } } } @@ -224,14 +235,14 @@ private void processUrlTemplate(MessageContext synCtx) throws VariableExpansionE (propertyKey.toString().startsWith(RESTConstants.REST_URI_VARIABLE_PREFIX) || propertyKey.toString().startsWith(RESTConstants.REST_QUERY_PARAM_PREFIX))) { Object objProperty = - synCtx.getProperty(propertyKey.toString()); + synCtx.getProperty(propertyKey.toString()); if (objProperty != null) { if (objProperty instanceof String) { variables.put(propertyKey.toString(), - (String) synCtx.getProperty(propertyKey.toString())); + (String) synCtx.getProperty(propertyKey.toString())); } else { variables.put(propertyKey.toString(), - (String) String.valueOf(synCtx.getProperty(propertyKey.toString()))); + (String) String.valueOf(synCtx.getProperty(propertyKey.toString()))); } } } @@ -282,14 +293,7 @@ private void processUrlTemplate(MessageContext synCtx) throws VariableExpansionE } } } - - - if (evaluatedUri != null) { - synCtx.setTo(new EndpointReference(evaluatedUri)); - if (super.getDefinition() != null) { - synCtx.setProperty(EndpointDefinition.DYNAMIC_URL_VALUE, evaluatedUri); - } - } + return evaluatedUri; } /** diff --git a/modules/core/src/main/java/org/apache/synapse/endpoints/OAuthConfiguredHTTPEndpoint.java b/modules/core/src/main/java/org/apache/synapse/endpoints/OAuthConfiguredHTTPEndpoint.java index adac33de58..2e218b2983 100644 --- a/modules/core/src/main/java/org/apache/synapse/endpoints/OAuthConfiguredHTTPEndpoint.java +++ b/modules/core/src/main/java/org/apache/synapse/endpoints/OAuthConfiguredHTTPEndpoint.java @@ -18,7 +18,9 @@ package org.apache.synapse.endpoints; +import com.damnhandy.uri.template.VariableExpansionException; import org.apache.axis2.AxisFault; +import org.apache.axis2.addressing.EndpointReference; import org.apache.synapse.MessageContext; import org.apache.synapse.SynapseConstants; import org.apache.synapse.endpoints.auth.AuthHandler; @@ -46,6 +48,7 @@ public OAuthConfiguredHTTPEndpoint(AuthHandler authHandler) { public void send(MessageContext synCtx) { try { + setResolvedUrlTemplate(synCtx); oAuthHandler.setAuthHeader(synCtx); // If this a blocking call, add 401 as a non error http status code @@ -77,17 +80,22 @@ public void send(MessageContext synCtx) { */ public MessageContext retryCallWithNewToken(MessageContext synCtx) { // remove the existing token from the cache so that a new token is generated - oAuthHandler.removeTokenFromCache(); - // set RETRIED_ON_OAUTH_FAILURE property to true - synCtx.setProperty(AuthConstants.RETRIED_ON_OAUTH_FAILURE, true); - send(synCtx); + try { + // set RETRIED_ON_OAUTH_FAILURE property to true + synCtx.setProperty(AuthConstants.RETRIED_ON_OAUTH_FAILURE, true); + oAuthHandler.removeTokenFromCache(synCtx); + send(synCtx); + } catch (AuthException e) { + handleError(synCtx, + "Error removing access token for oauth configured http endpoint " + this.getName(), e); + } return synCtx; } @Override public void destroy() { - oAuthHandler.removeTokenFromCache(); + oAuthHandler.removeTokensFromCache(); super.destroy(); } @@ -109,4 +117,21 @@ private void handleError(MessageContext synCtx, String message, Exception except log.error(errorMsg); informFailure(synCtx, SynapseConstants.ENDPOINT_AUTH_FAILURE, errorMsg); } + + private void setResolvedUrlTemplate(MessageContext messageContext) { + String resolvedUrl = resolveUrlTemplate(messageContext); + if (resolvedUrl != null) { + messageContext.setTo(new EndpointReference(resolvedUrl)); + if (super.getDefinition() != null) { + messageContext.setProperty(EndpointDefinition.DYNAMIC_URL_VALUE, resolvedUrl); + } + } + } + + @Override + protected void processUrlTemplate(MessageContext synCtx) throws VariableExpansionException { + // Since we set the resolved URL in the OAuthConfiguredHTTPEndpoint.send method + // return the processUrlTemplate to skip re-resolving at HTTPEndpoint. + return; + } } diff --git a/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/AuthorizationCodeHandler.java b/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/AuthorizationCodeHandler.java index 7d6c4a5800..dc3432e41a 100644 --- a/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/AuthorizationCodeHandler.java +++ b/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/AuthorizationCodeHandler.java @@ -26,6 +26,8 @@ import org.apache.synapse.endpoints.auth.AuthConstants; import org.apache.synapse.endpoints.auth.AuthException; +import java.util.Objects; + /** * This class is used to handle Authorization code grant oauth. */ @@ -74,6 +76,15 @@ protected OMElement serializeSpecificOAuthConfigs(OMFactory omFactory) { return authCode; } + @Override + protected int getHash(MessageContext messageContext) throws AuthException { + return Objects.hash(messageContext.getTo().getAddress(), OAuthUtils.resolveExpression(getTokenUrl(), messageContext), + OAuthUtils.resolveExpression(getClientId(), messageContext), OAuthUtils.resolveExpression(getClientSecret(), + messageContext), OAuthUtils.resolveExpression(getRefreshToken(), messageContext), + getRequestParametersAsString(messageContext), getResolvedCustomHeadersMap(getCustomHeadersMap(), + messageContext)); + } + /** * Return the refresh token secret relevant to the Authorization Code Handler. * diff --git a/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/ClientCredentialsHandler.java b/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/ClientCredentialsHandler.java index 2a1d21831e..df95cf1e81 100644 --- a/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/ClientCredentialsHandler.java +++ b/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/ClientCredentialsHandler.java @@ -26,6 +26,8 @@ import org.apache.synapse.endpoints.auth.AuthConstants; import org.apache.synapse.endpoints.auth.AuthException; +import java.util.Objects; + /** * This class is used to handle Client Credentials grant oauth. */ @@ -60,4 +62,12 @@ protected OMElement serializeSpecificOAuthConfigs(OMFactory omFactory) { return omFactory.createOMElement(AuthConstants.CLIENT_CREDENTIALS, SynapseConstants.SYNAPSE_OMNAMESPACE); } + + @Override + protected int getHash(MessageContext messageContext) throws AuthException { + return Objects.hash(messageContext.getTo().getAddress(), OAuthUtils.resolveExpression(getTokenUrl(), messageContext), + OAuthUtils.resolveExpression(getClientId(), messageContext), OAuthUtils.resolveExpression(getClientSecret(), + messageContext), getRequestParametersAsString(messageContext), + getResolvedCustomHeadersMap(getCustomHeadersMap(), messageContext)); + } } diff --git a/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/OAuthHandler.java b/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/OAuthHandler.java index 83ddf7ca21..b9d8f957da 100644 --- a/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/OAuthHandler.java +++ b/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/OAuthHandler.java @@ -46,7 +46,6 @@ public abstract class OAuthHandler implements AuthHandler { private final String id; - private final String tokenApiUrl; private final String clientId; private final String clientSecret; @@ -89,7 +88,7 @@ public void setAuthHeader(MessageContext messageContext) throws AuthException { private String getToken(final MessageContext messageContext) throws AuthException { try { - return TokenCache.getInstance().getToken(id, new Callable() { + return TokenCache.getInstance().getToken(getId(messageContext), new Callable() { @Override public String call() throws AuthException, IOException { return OAuthClient.generateToken(OAuthUtils.resolveExpression(tokenApiUrl, messageContext), @@ -129,12 +128,20 @@ public int compare(String o1, String o2) { } } + /** + * Method to remove the token from the cache when the token is invalid. + */ + public void removeTokenFromCache(MessageContext messageContext) throws AuthException { + + TokenCache.getInstance().removeToken(getId(messageContext)); + } + /** * Method to remove the token from the cache when the endpoint is destroyed. */ - public void removeTokenFromCache() { + public void removeTokensFromCache() { - TokenCache.getInstance().removeToken(id); + TokenCache.getInstance().removeTokens(id.concat("_")); } /** @@ -313,7 +320,7 @@ public void setCustomHeaders(Map customHeadersMap) { * @param messageContext Message Context of the request which will be used to resolve dynamic expressions * @return Map Resolved custom headers */ - private Map getResolvedCustomHeadersMap(Map customHeadersMap, + protected Map getResolvedCustomHeadersMap(Map customHeadersMap, MessageContext messageContext) throws AuthException { Map resolvedCustomHeadersMap = null; @@ -339,4 +346,9 @@ public int getSocketTimeout() { return socketTimeout; } + protected abstract int getHash(MessageContext messageContext) throws AuthException; + + private String getId(MessageContext messageContext) throws AuthException { + return id.concat("_").concat(String.valueOf(getHash(messageContext))); + } } diff --git a/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/PasswordCredentialsHandler.java b/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/PasswordCredentialsHandler.java index 928fce7d5c..07f48e2916 100644 --- a/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/PasswordCredentialsHandler.java +++ b/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/PasswordCredentialsHandler.java @@ -26,6 +26,8 @@ import org.apache.synapse.endpoints.auth.AuthConstants; import org.apache.synapse.endpoints.auth.AuthException; +import java.util.Objects; + /** * This class is used to handle Password Credentials grant oauth. */ @@ -78,6 +80,16 @@ protected OMElement serializeSpecificOAuthConfigs(OMFactory omFactory) { return passwordCredentials; } + @Override + protected int getHash(MessageContext messageContext) throws AuthException { + return Objects.hash(messageContext.getTo().getAddress(), OAuthUtils.resolveExpression(getTokenUrl(), messageContext), + OAuthUtils.resolveExpression(getClientId(), messageContext), OAuthUtils.resolveExpression(getClientSecret(), + messageContext), OAuthUtils.resolveExpression(getUsername(), messageContext), + OAuthUtils.resolveExpression(getPassword(), messageContext), + getRequestParametersAsString(messageContext), getResolvedCustomHeadersMap(getCustomHeadersMap(), + messageContext)); + } + public String getUsername() { return username; diff --git a/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/TokenCache.java b/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/TokenCache.java index 61fc973d60..6537c11db3 100644 --- a/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/TokenCache.java +++ b/modules/core/src/main/java/org/apache/synapse/endpoints/auth/oauth/TokenCache.java @@ -82,7 +82,7 @@ public String getToken(String id, Callable callable) throws ExecutionExc } /** - * This method is called to remove the token from the cache when the endpoint is destroyed + * This method is called to remove the token from the cache when the token is invalid * * @param id id of the endpoint */ @@ -91,4 +91,12 @@ public void removeToken(String id) { tokenMap.invalidate(id); } + /** + * This method is called to remove the tokens from the cache when the endpoint is destroyed + * + * @param oauthHandlerId id of the OAuth handler bounded to the endpoint + */ + public void removeTokens(String oauthHandlerId) { + tokenMap.asMap().entrySet().removeIf(entry -> entry.getKey().startsWith(oauthHandlerId)); + } } diff --git a/modules/core/src/main/java/org/apache/synapse/util/InlineExpressionUtil.java b/modules/core/src/main/java/org/apache/synapse/util/InlineExpressionUtil.java index 78157194b8..e340cc87e3 100644 --- a/modules/core/src/main/java/org/apache/synapse/util/InlineExpressionUtil.java +++ b/modules/core/src/main/java/org/apache/synapse/util/InlineExpressionUtil.java @@ -92,6 +92,14 @@ private static String replaceValue(MessageContext messageContext, String text, b Matcher matcher = EXPRESSION_PATTERN.matcher(text); while (matcher.find()) { String matchSeq = matcher.group(); + String surroundedString; + try { + surroundedString = text.substring(text.indexOf(matchSeq) - 1, text.indexOf(matchSeq) + matchSeq + .length() + 1); + } catch (IndexOutOfBoundsException e) { + // catch index out of bound exception when the expression is at the beginning or end of the text + surroundedString = StringUtils.EMPTY; + } String value = getDynamicValue(messageContext, matchSeq.substring(1, matchSeq.length() - 1)); if (value == null) { value = StringUtils.EMPTY; @@ -99,7 +107,7 @@ private static String replaceValue(MessageContext messageContext, String text, b // If the string is neither XML or JSON, it is considered a String and must be wrapped in double quotes // If it is an empty string returned from a json-eval expression it must be wrapped in double quotes if (isInline && ((value.isEmpty() && matchSeq.contains(EXPRESSION_JSON_EVAL)) - || (!isValidXML(value) && !isValidJson(value)))) { + || (!isValidXML(value) && !isValidJson(value) && !isSurroundedByQuotes(surroundedString)))) { value = "\"" + value + "\""; } text = text.replace(matchSeq, value); @@ -107,6 +115,10 @@ private static String replaceValue(MessageContext messageContext, String text, b return text; } + private static boolean isSurroundedByQuotes(String text) { + + return text.startsWith("\"") && text.endsWith("\""); + } /** * Replaces Dynamic Values represented by expressions inside json-eval. * diff --git a/modules/coverage-report/pom.xml b/modules/coverage-report/pom.xml index 7fdde64b1b..0dadd42316 100644 --- a/modules/coverage-report/pom.xml +++ b/modules/coverage-report/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/distribution/pom.xml b/modules/distribution/pom.xml index c9e3421161..259f97bb83 100644 --- a/modules/distribution/pom.xml +++ b/modules/distribution/pom.xml @@ -22,7 +22,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/experimental/pom.xml b/modules/experimental/pom.xml index b6721038bd..8218aed984 100644 --- a/modules/experimental/pom.xml +++ b/modules/experimental/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/extensions/pom.xml b/modules/extensions/pom.xml index 0cb332769e..8caf399983 100644 --- a/modules/extensions/pom.xml +++ b/modules/extensions/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/features/org.apache.synapse.samples.feature/pom.xml b/modules/features/org.apache.synapse.samples.feature/pom.xml index 2303377d32..7e95924648 100644 --- a/modules/features/org.apache.synapse.samples.feature/pom.xml +++ b/modules/features/org.apache.synapse.samples.feature/pom.xml @@ -19,7 +19,7 @@ synapse-features - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT org.apache.synapse diff --git a/modules/features/org.apache.synapse.transport.fix.feature/pom.xml b/modules/features/org.apache.synapse.transport.fix.feature/pom.xml index e3a7a78c76..ecb4e10197 100644 --- a/modules/features/org.apache.synapse.transport.fix.feature/pom.xml +++ b/modules/features/org.apache.synapse.transport.fix.feature/pom.xml @@ -19,7 +19,7 @@ synapse-features - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT org.apache.synapse diff --git a/modules/features/org.apache.synapse.transport.nhttp.feature/pom.xml b/modules/features/org.apache.synapse.transport.nhttp.feature/pom.xml index a444d02d4d..308127b2e3 100644 --- a/modules/features/org.apache.synapse.transport.nhttp.feature/pom.xml +++ b/modules/features/org.apache.synapse.transport.nhttp.feature/pom.xml @@ -19,7 +19,7 @@ synapse-features - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT org.apache.synapse diff --git a/modules/features/org.apache.synapse.transport.vfs.feature/pom.xml b/modules/features/org.apache.synapse.transport.vfs.feature/pom.xml index 89a448d423..3cc18abeb9 100644 --- a/modules/features/org.apache.synapse.transport.vfs.feature/pom.xml +++ b/modules/features/org.apache.synapse.transport.vfs.feature/pom.xml @@ -19,7 +19,7 @@ synapse-features - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT org.apache.synapse diff --git a/modules/features/org.apache.synapse.transport.vfs.smb.feature/pom.xml b/modules/features/org.apache.synapse.transport.vfs.smb.feature/pom.xml index 59a21db186..cdf4b9e60c 100644 --- a/modules/features/org.apache.synapse.transport.vfs.smb.feature/pom.xml +++ b/modules/features/org.apache.synapse.transport.vfs.smb.feature/pom.xml @@ -19,7 +19,7 @@ synapse-features - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT org.apache.synapse diff --git a/modules/features/org.apache.synapse.transport.vfs.smb2.feature/pom.xml b/modules/features/org.apache.synapse.transport.vfs.smb2.feature/pom.xml index 27bd89acd5..0dca2ed56c 100644 --- a/modules/features/org.apache.synapse.transport.vfs.smb2.feature/pom.xml +++ b/modules/features/org.apache.synapse.transport.vfs.smb2.feature/pom.xml @@ -20,7 +20,7 @@ synapse-features org.apache.synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT 4.0.0 diff --git a/modules/features/org.apache.synapse.wso2.feature/pom.xml b/modules/features/org.apache.synapse.wso2.feature/pom.xml index 0f4d06d7fa..a2e80eda68 100644 --- a/modules/features/org.apache.synapse.wso2.feature/pom.xml +++ b/modules/features/org.apache.synapse.wso2.feature/pom.xml @@ -20,7 +20,7 @@ synapse-features org.apache.synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT 4.0.0 diff --git a/modules/features/pom.xml b/modules/features/pom.xml index 7acc6b5faf..57bc3a74ee 100644 --- a/modules/features/pom.xml +++ b/modules/features/pom.xml @@ -22,7 +22,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/handler/pom.xml b/modules/handler/pom.xml index be1ff98b84..1ffe1d6512 100644 --- a/modules/handler/pom.xml +++ b/modules/handler/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/integration/pom.xml b/modules/integration/pom.xml index 951b61bf6f..461d91801e 100644 --- a/modules/integration/pom.xml +++ b/modules/integration/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/integration/src/test/java/org/apache/synapse/samples/framework/tests/endpoint/Sample63.java b/modules/integration/src/test/java/org/apache/synapse/samples/framework/tests/endpoint/Sample63.java index caede6b321..5ef71a4029 100644 --- a/modules/integration/src/test/java/org/apache/synapse/samples/framework/tests/endpoint/Sample63.java +++ b/modules/integration/src/test/java/org/apache/synapse/samples/framework/tests/endpoint/Sample63.java @@ -100,4 +100,27 @@ public void testOAuthConfiguredEPWithDynamicValues() throws Exception { HttpResponse response = client.doGet("http://127.0.0.1:8280/foodapi/list/dynamicValues"); assertEquals(HttpStatus.SC_OK, response.getStatus()); } + + public void testOAuthConfiguredDynamicURLEP() throws Exception { + + String payload1 = "\n" + + "\thttp://localhost:9000/foodservice/food\n" + + "\thttp://localhost:9000/foodservice/token1\n" + + ""; + + String payload2 = "\n" + + "\thttp://localhost:9000/foodservice/apple\n" + + "\thttp://localhost:9000/foodservice/token2\n" + + ""; + + BasicHttpClient client = new BasicHttpClient(); + HttpResponse response = client.doPost("http://127.0.0.1:8280/foodapi/list/dynamicURL", payload1.getBytes(), + "text/xml"); + assertEquals(HttpStatus.SC_OK, response.getStatus()); + + HttpResponse response2 = client.doPost("http://127.0.0.1:8280/foodapi/list/dynamicURL", payload2.getBytes(), + "text/xml"); + assertEquals(HttpStatus.SC_OK, response2.getStatus()); + assertEquals("1", response2.getBodyAsString()); + } } diff --git a/modules/migrator/pom.xml b/modules/migrator/pom.xml index 24f4d3b21c..46bda83af9 100644 --- a/modules/migrator/pom.xml +++ b/modules/migrator/pom.xml @@ -24,7 +24,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/packaging/package-archetype/pom.xml b/modules/packaging/package-archetype/pom.xml index 6da7f95f06..8be2118e74 100644 --- a/modules/packaging/package-archetype/pom.xml +++ b/modules/packaging/package-archetype/pom.xml @@ -22,7 +22,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../../pom.xml synapse-package-archetype diff --git a/modules/packaging/package-skeleton/pom.xml b/modules/packaging/package-skeleton/pom.xml index 5030966654..15a17be3af 100644 --- a/modules/packaging/package-skeleton/pom.xml +++ b/modules/packaging/package-skeleton/pom.xml @@ -22,7 +22,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../../pom.xml synapse-package-skeleton diff --git a/modules/patches/pom.xml b/modules/patches/pom.xml index f6631be765..a6ebd0ace3 100644 --- a/modules/patches/pom.xml +++ b/modules/patches/pom.xml @@ -22,7 +22,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml synapse-patches diff --git a/modules/samples/pom.xml b/modules/samples/pom.xml index 2af5c2a0db..68d614a5f7 100644 --- a/modules/samples/pom.xml +++ b/modules/samples/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/samples/src/main/java/org/wso2/synapse/samples/jaxrs/foodsample/Constants.java b/modules/samples/src/main/java/org/wso2/synapse/samples/jaxrs/foodsample/Constants.java index cf2b5d7920..ca30a72085 100644 --- a/modules/samples/src/main/java/org/wso2/synapse/samples/jaxrs/foodsample/Constants.java +++ b/modules/samples/src/main/java/org/wso2/synapse/samples/jaxrs/foodsample/Constants.java @@ -22,6 +22,7 @@ public class Constants { static final String refreshToken = "wxyz#9876"; static final String accessToken = "abcd@1234"; + static final String accessToken2 = "jklm#6789"; static final String expiresIn = "3600"; static final String tokenType = "Bearer"; static final String clientId = "my_client_id"; diff --git a/modules/samples/src/main/java/org/wso2/synapse/samples/jaxrs/foodsample/FoodService.java b/modules/samples/src/main/java/org/wso2/synapse/samples/jaxrs/foodsample/FoodService.java index 6a2826e6e6..e95167e85b 100644 --- a/modules/samples/src/main/java/org/wso2/synapse/samples/jaxrs/foodsample/FoodService.java +++ b/modules/samples/src/main/java/org/wso2/synapse/samples/jaxrs/foodsample/FoodService.java @@ -38,6 +38,7 @@ public class FoodService { private int unauthorizedReqCount = 0; private int tokenReqCount = 0; + private int appleServiceRequests = 0; @POST @Path("/token") @@ -57,6 +58,38 @@ public Response getAccessToken(@Context HttpHeaders httpHeaders, return Response.status(Response.Status.UNAUTHORIZED).entity("Invalid Credentials").build(); } + @POST + @Path("/token1") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.APPLICATION_JSON) + public Response getAccessToken1(@Context HttpHeaders httpHeaders, + MultivaluedMap tokenRequestParams) { + + String basicHeader = httpHeaders.getHeaderString("Authorization"); + + if (validateBasicAuthHeader(basicHeader)) { + return Response.status(Response.Status.OK).entity(new Token(Constants.accessToken, Constants.expiresIn, + Constants.tokenType)).build(); + } + return Response.status(Response.Status.UNAUTHORIZED).entity("Invalid Credentials").build(); + } + + @POST + @Path("/token2") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.APPLICATION_JSON) + public Response getAccessToken2(@Context HttpHeaders httpHeaders, + MultivaluedMap tokenRequestParams) { + + String basicHeader = httpHeaders.getHeaderString("Authorization"); + + if (validateBasicAuthHeader(basicHeader)) { + return Response.status(Response.Status.OK).entity(new Token(Constants.accessToken2, Constants.expiresIn, + Constants.tokenType)).build(); + } + return Response.status(Response.Status.UNAUTHORIZED).entity("Invalid Credentials").build(); + } + @POST @Path("/custom-token") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @@ -105,6 +138,22 @@ public Response getFoodItem(@Context HttpHeaders httpHeaders) { return Response.status(Response.Status.UNAUTHORIZED).build(); } + @GET + @Path("/apple") + @Produces(MediaType.APPLICATION_JSON) + public Response getFoodItem2(@Context HttpHeaders httpHeaders) { + + appleServiceRequests++; + String authorizationHeader = httpHeaders.getHeaderString("Authorization"); + if (authorizationHeader != null) { + String token = authorizationHeader.split(" ")[1]; + if (token.equals(Constants.accessToken2)) { + return Response.status(Response.Status.OK).entity(appleServiceRequests).build(); + } + } + return Response.status(Response.Status.UNAUTHORIZED).build(); + } + @GET @Path("/unauthorized") @Produces(MediaType.APPLICATION_JSON) diff --git a/modules/securevault/pom.xml b/modules/securevault/pom.xml index 6c5af61d53..8d13eda926 100644 --- a/modules/securevault/pom.xml +++ b/modules/securevault/pom.xml @@ -24,7 +24,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml org.apache.synapse diff --git a/modules/tasks/pom.xml b/modules/tasks/pom.xml index 6c51e3189c..107475619f 100644 --- a/modules/tasks/pom.xml +++ b/modules/tasks/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/transports/core/nhttp/pom.xml b/modules/transports/core/nhttp/pom.xml index ffd57040d6..71a011389f 100644 --- a/modules/transports/core/nhttp/pom.xml +++ b/modules/transports/core/nhttp/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse synapse-transports - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/CertificateVerificationManager.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/CertificateVerificationManager.java new file mode 100644 index 0000000000..2765ac41a1 --- /dev/null +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/CertificateVerificationManager.java @@ -0,0 +1,287 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.synapse.transport.certificatevalidation; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.synapse.transport.certificatevalidation.cache.CertCache; +import org.apache.synapse.transport.certificatevalidation.crl.CRLCache; +import org.apache.synapse.transport.certificatevalidation.crl.CRLVerifier; +import org.apache.synapse.transport.certificatevalidation.ocsp.OCSPCache; +import org.apache.synapse.transport.certificatevalidation.ocsp.OCSPVerifier; +import org.apache.synapse.transport.certificatevalidation.pathvalidation.CertificatePathValidator; +import org.apache.synapse.transport.nhttp.config.TrustStoreHolder; + +import java.io.ByteArrayInputStream; +import java.security.InvalidKeyException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SignatureException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Optional; + +/** + * Manager class responsible for verifying certificates. This class will use the available verifiers according to + * a predefined policy. + */ +public class CertificateVerificationManager { + + private int cacheSize = Constants.CACHE_DEFAULT_ALLOCATED_SIZE; + private int cacheDelayMins = Constants.CACHE_DEFAULT_DELAY_MINS; + private boolean isFullCertChainValidationEnabled = true; + private boolean isCertExpiryValidationEnabled = false; + private static final Log log = LogFactory.getLog(CertificateVerificationManager.class); + + public CertificateVerificationManager(Integer cacheAllocatedSize, Integer cacheDelayMins) { + + if (cacheAllocatedSize != null && cacheAllocatedSize > Constants.CACHE_MIN_ALLOCATED_SIZE + && cacheAllocatedSize < Constants.CACHE_MAX_ALLOCATED_SIZE) { + this.cacheSize = cacheAllocatedSize; + } else { + log.warn("The cache size is out of range. Hence, using the default cache size value of " + + Constants.CACHE_DEFAULT_ALLOCATED_SIZE + "."); + } + if (cacheDelayMins != null && cacheDelayMins > Constants.CACHE_MIN_DELAY_MINS + && cacheDelayMins < Constants.CACHE_MAX_DELAY_MINS) { + this.cacheDelayMins = cacheDelayMins; + } else { + log.warn("The cache delay is out of range. Hence, using the default cache delay value of " + + Constants.CACHE_DEFAULT_DELAY_MINS + "."); + } + } + + public CertificateVerificationManager(Integer cacheAllocatedSize, Integer cacheDelayMins, + boolean isFullCertChainValidationEnabled, + boolean isCertExpiryValidationEnabled) { + + if (cacheAllocatedSize != null && cacheAllocatedSize > Constants.CACHE_MIN_ALLOCATED_SIZE + && cacheAllocatedSize < Constants.CACHE_MAX_ALLOCATED_SIZE) { + this.cacheSize = cacheAllocatedSize; + } else { + log.warn("The cache size is out of range. Hence, using the default cache size value of " + + Constants.CACHE_DEFAULT_ALLOCATED_SIZE + "."); + } + if (cacheDelayMins != null && cacheDelayMins > Constants.CACHE_MIN_DELAY_MINS + && cacheDelayMins < Constants.CACHE_MAX_DELAY_MINS) { + this.cacheDelayMins = cacheDelayMins; + } else { + log.warn("The cache delay is out of range. Hence, using the default cache delay value of " + + Constants.CACHE_DEFAULT_DELAY_MINS + "."); + } + + this.isFullCertChainValidationEnabled = isFullCertChainValidationEnabled; + this.isCertExpiryValidationEnabled = isCertExpiryValidationEnabled; + } + + /** + * This method verifies the given certificate chain or given peer certificate for revocation based on the + * requirement of full certificate chain validation. If full chain validation is enabled (default), + * the full certificate chain will be validated before checking the chain for revocation. If full chain validation + * is disabled, this method expects a single peer certificate, and it is validated with the immediate issuer + * certificate in the truststore (The truststore must contain the immediate issuer of the peer certificate). + * In both cases, OCSP and CRL verifiers are used for revocation verification. + * It first tries to verify using OCSP since OCSP verification is faster. If that fails it tries to do the + * verification using CRL. + * + * @param peerCertificates javax.security.cert.X509Certificate[] array of peer certificate chain from peer/client. + * @throws CertificateVerificationException + */ + public void verifyCertificateValidity(javax.security.cert.X509Certificate[] peerCertificates) + throws CertificateVerificationException { + + X509Certificate[] convertedCertificates = convert(peerCertificates); + + Optional peerCertOpt; + X509Certificate peerCert = null; + X509Certificate issuerCert = null; + String alias; + + if (!isFullCertChainValidationEnabled) { + + if (log.isDebugEnabled()) { + log.debug("Retrieving the issuer certificate from client truststore since full certificate chain " + + "validation is disabled"); + } + + KeyStore trustStore = TrustStoreHolder.getInstance().getClientTrustStore(); + Enumeration aliases; + + // When full chain validation is disabled, only one cert is expected + peerCertOpt = Arrays.stream(convertedCertificates).findFirst(); + if (peerCertOpt.isPresent()) { + peerCert = peerCertOpt.get(); + } else { + throw new CertificateVerificationException("Peer certificate is not provided"); + } + + // Get cert cache and initialize it + CertCache certCache = CertCache.getCache(); + + if (certCache.getCacheValue(peerCert.getSerialNumber().toString()) == null) { + + try { + aliases = trustStore.aliases(); + } catch (KeyStoreException e) { + throw new CertificateVerificationException("Error while retrieving aliases from truststore", e); + } + + while (aliases.hasMoreElements()) { + + alias = aliases.nextElement(); + try { + issuerCert = (X509Certificate) trustStore.getCertificate(alias); + } catch (KeyStoreException e) { + throw new CertificateVerificationException("Unable to read the certificate from " + + "truststore with the alias: " + alias, e); + } + + if (issuerCert == null) { + throw new CertificateVerificationException("Issuer certificate not found in truststore"); + } + + try { + peerCert.verify(issuerCert.getPublicKey()); + + log.debug("Valid issuer certificate found in the client truststore. Caching.."); + + // Store the valid issuer cert in cache for future use + certCache.setCacheValue(peerCert.getSerialNumber().toString(), issuerCert); + if (log.isDebugEnabled()) { + log.debug("Issuer certificate with serial number: " + issuerCert.getSerialNumber() + .toString() + " has been cached against the serial number: " + peerCert + .getSerialNumber().toString() + " of the peer certificate."); + } + break; + } catch (SignatureException | CertificateException | NoSuchAlgorithmException | + InvalidKeyException | NoSuchProviderException e) { + // Unable to verify the signature. Check with the next certificate in the next loop traversal. + } + } + } else { + X509Certificate cachedIssuerCert = certCache.getCacheValue(peerCert.getSerialNumber().toString()); + try { + peerCert.verify(cachedIssuerCert.getPublicKey()); + } catch (SignatureException | CertificateException | NoSuchAlgorithmException | + InvalidKeyException | + NoSuchProviderException e) { + // Unable to verify the signature. + throw new CertificateVerificationException("Unable to verify the signature of the certificate."); + } + } + } + + OCSPCache ocspCache = OCSPCache.getCache(cacheSize, cacheDelayMins); + CRLCache crlCache = CRLCache.getCache(cacheSize, cacheDelayMins); + + RevocationVerifier[] verifiers = {new OCSPVerifier(ocspCache), new CRLVerifier(crlCache)}; + + for (RevocationVerifier verifier : verifiers) { + try { + if (isFullCertChainValidationEnabled) { + + if (isCertExpiryValidationEnabled) { + log.debug("Validating certificate chain for expiry"); + if (isExpired(convertedCertificates)) { + throw new CertificateVerificationException("One of the provided certificates are expired"); + } + } + + log.debug("Doing full certificate chain validation"); + CertificatePathValidator pathValidator = new CertificatePathValidator(convertedCertificates, + verifier); + pathValidator.validatePath(); + } else { + + if (isCertExpiryValidationEnabled) { + log.debug("Validating the client certificate for expiry"); + if (isExpired(convertedCertificates)) { + throw new CertificateVerificationException("The provided certificate is expired"); + } + } + + log.debug("Validating client certificate with the issuer certificate retrieved from" + + "the trust store"); + verifier.checkRevocationStatus(peerCert, issuerCert); + } + return; + } catch (Exception e) { + log.debug("Certificate verification with " + verifier.getClass().getSimpleName() + " failed. ", e); + } + } + throw new CertificateVerificationException("Path Verification Failed for both OCSP and CRL"); + } + + /** + * @param certs array of javax.security.cert.X509Certificate[] s. + * @return the converted array of java.security.cert.X509Certificate[] s. + * @throws CertificateVerificationException + */ + private X509Certificate[] convert(javax.security.cert.X509Certificate[] certs) + throws CertificateVerificationException { + X509Certificate[] certChain = new X509Certificate[certs.length]; + Throwable exceptionThrown; + for (int i = 0; i < certs.length; i++) { + try { + byte[] encoded = certs[i].getEncoded(); + ByteArrayInputStream bis = new ByteArrayInputStream(encoded); + java.security.cert.CertificateFactory cf + = java.security.cert.CertificateFactory.getInstance("X.509"); + certChain[i]=((X509Certificate)cf.generateCertificate(bis)); + continue; + } catch (java.security.cert.CertificateEncodingException e) { + exceptionThrown = e; + } catch (javax.security.cert.CertificateEncodingException e) { + exceptionThrown = e; + } catch (java.security.cert.CertificateException e) { + exceptionThrown = e; + } + throw new CertificateVerificationException("Cant Convert certificates from javax to java", exceptionThrown); + } + return certChain; + } + + /** + * Checks whether a provided certificate is expired or not at the time it is validated. + * + * @param certificates certificates to be validated for expiry + * @return true if one of the certs are expired, false otherwise + */ + public boolean isExpired(X509Certificate[] certificates) { + + for (X509Certificate cert : certificates) { + try { + cert.checkValidity(); + } catch (CertificateExpiredException e) { + log.error("Peer certificate is expired"); + return true; + } catch (CertificateNotYetValidException e) { + log.error("Peer certificate is not valid yet"); + return true; + } + } + return false; + } +} diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/RevocationVerificationManager.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/RevocationVerificationManager.java deleted file mode 100644 index 338b8a8a36..0000000000 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/RevocationVerificationManager.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.synapse.transport.certificatevalidation; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.synapse.transport.certificatevalidation.crl.CRLCache; -import org.apache.synapse.transport.certificatevalidation.crl.CRLVerifier; -import org.apache.synapse.transport.certificatevalidation.ocsp.OCSPCache; -import org.apache.synapse.transport.certificatevalidation.ocsp.OCSPVerifier; -import org.apache.synapse.transport.certificatevalidation.pathvalidation.CertificatePathValidator; - -import java.io.ByteArrayInputStream; -import java.security.cert.X509Certificate; - -/** - * Manager class responsible for verifying certificates. This class will use the available verifiers according to - * a predefined policy. - */ -public class RevocationVerificationManager { - - private int cacheSize = Constants.CACHE_DEFAULT_ALLOCATED_SIZE; - private int cacheDelayMins = Constants.CACHE_DEFAULT_DELAY_MINS; - private static final Log log = LogFactory.getLog(RevocationVerificationManager.class); - - public RevocationVerificationManager(Integer cacheAllocatedSize, Integer cacheDelayMins) { - - if (cacheAllocatedSize != null && cacheAllocatedSize > Constants.CACHE_MIN_ALLOCATED_SIZE - && cacheAllocatedSize < Constants.CACHE_MAX_ALLOCATED_SIZE) { - this.cacheSize = cacheAllocatedSize; - } - if (cacheDelayMins != null && cacheDelayMins > Constants.CACHE_MIN_DELAY_MINS - && cacheDelayMins < Constants.CACHE_MAX_DELAY_MINS) { - this.cacheDelayMins = cacheDelayMins; - } - } - - /** - * This method first tries to verify the given certificate chain using OCSP since OCSP verification is - * faster. If that fails it tries to do the verification using CRL. - * @param peerCertificates javax.security.cert.X509Certificate[] array of peer certificate chain from peer/client. - * @throws CertificateVerificationException - */ - public void verifyRevocationStatus(javax.security.cert.X509Certificate[] peerCertificates) - throws CertificateVerificationException { - - X509Certificate[] convertedCertificates = convert(peerCertificates); - - long start = System.currentTimeMillis(); - - OCSPCache ocspCache = OCSPCache.getCache(); - ocspCache.init(cacheSize, cacheDelayMins); - CRLCache crlCache = CRLCache.getCache(); - crlCache.init(cacheSize, cacheDelayMins); - - RevocationVerifier[] verifiers = {new OCSPVerifier(ocspCache), new CRLVerifier(crlCache)}; - - for (RevocationVerifier verifier : verifiers) { - try { - CertificatePathValidator pathValidator = new CertificatePathValidator(convertedCertificates, verifier); - pathValidator.validatePath(); - log.info("Path verification Successful. Took " + (System.currentTimeMillis() - start) + " ms."); - return; - } catch (Exception e) { - log.info(verifier.getClass().getSimpleName() + " failed."); - log.debug("Certificate verification with " + verifier.getClass().getSimpleName() + " failed. ", e); - } - } - throw new CertificateVerificationException("Path Verification Failed for both OCSP and CRL"); - } - - /** - * @param certs array of javax.security.cert.X509Certificate[] s. - * @return the converted array of java.security.cert.X509Certificate[] s. - * @throws CertificateVerificationException - */ - private X509Certificate[] convert(javax.security.cert.X509Certificate[] certs) - throws CertificateVerificationException { - X509Certificate[] certChain = new X509Certificate[certs.length]; - Throwable exceptionThrown; - for (int i = 0; i < certs.length; i++) { - try { - byte[] encoded = certs[i].getEncoded(); - ByteArrayInputStream bis = new ByteArrayInputStream(encoded); - java.security.cert.CertificateFactory cf - = java.security.cert.CertificateFactory.getInstance("X.509"); - certChain[i]=((X509Certificate)cf.generateCertificate(bis)); - continue; - } catch (java.security.cert.CertificateEncodingException e) { - exceptionThrown = e; - } catch (javax.security.cert.CertificateEncodingException e) { - exceptionThrown = e; - } catch (java.security.cert.CertificateException e) { - exceptionThrown = e; - } - throw new CertificateVerificationException("Cant Convert certificates from javax to java", exceptionThrown); - } - return certChain; - } -} diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/cache/CertCache.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/cache/CertCache.java new file mode 100644 index 0000000000..9ada86e88d --- /dev/null +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/cache/CertCache.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.synapse.transport.certificatevalidation.cache; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.synapse.commons.jmx.MBeanRegistrar; +import org.apache.synapse.transport.certificatevalidation.Constants; + +import java.security.cert.X509Certificate; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * This is a cache to store a certificate against a unique string (can be a serial number). This is a singleton since + * more than one cache of this kind should not be allowed. This cache can be used by any place where certificate + * caching is needed. + */ +public class CertCache implements ManageableCache { + + private static volatile CertCache cache; + private static volatile Map hashMap = new ConcurrentHashMap(); + private static volatile Iterator> iterator = hashMap.entrySet().iterator(); + private static volatile CacheManager cacheManager; + private static final Log log = LogFactory.getLog(CertCache.class); + + private CertCache() { + } + + public static CertCache getCache() { + //Double-checked locking + if (cache == null) { + synchronized (CertCache.class) { + if (cache == null) { + cache = new CertCache(); + cacheManager = new CacheManager(cache, Constants.CACHE_DEFAULT_ALLOCATED_SIZE, + Constants.CACHE_DEFAULT_DELAY_MINS); + } + } + } + return cache; + } + + public synchronized X509Certificate getCacheValue(String serialNumber) { + CertCacheValue cacheValue = hashMap.get(serialNumber); + if (cacheValue != null) { + return cacheValue.getValue(); + } else + return null; + } + + @Override + public ManageableCacheValue getNextCacheValue() { + + if (iterator.hasNext()) { + return hashMap.get(iterator.next().getKey()); + } else { + resetIterator(); + return null; + } + } + + @Override + public int getCacheSize() { + + return hashMap.size(); + } + + @Override + public void resetIterator() { + + iterator = hashMap.entrySet().iterator(); + } + + public static void resetCache() { + + hashMap.clear(); + } + + public synchronized void setCacheValue(String serialNumber, X509Certificate cert) { + CertCacheValue cacheValue = new CertCacheValue(serialNumber, cert); + + if (log.isDebugEnabled()) { + log.debug("Before set - HashMap size " + hashMap.size()); + } + hashMap.put(serialNumber, cacheValue); + if (log.isDebugEnabled()) { + log.debug("After set - HashMap size " + hashMap.size()); + } + } + + public synchronized void removeCacheValue(String serialNumber) { + + if (log.isDebugEnabled()) { + log.debug("Before remove - HashMap size " + hashMap.size()); + } + hashMap.remove(serialNumber); + if (log.isDebugEnabled()) { + log.debug("After remove - HashMap size " + hashMap.size()); + } + } + + /** + * This is the wrapper class of the actual cache value. + */ + private class CertCacheValue implements ManageableCacheValue { + + private final String serialNumber; + private final X509Certificate issuerCertificate; + private final long timeStamp = System.currentTimeMillis(); + + public CertCacheValue(String serialNumber, X509Certificate issuerCertificate) { + + this.serialNumber = serialNumber; + this.issuerCertificate = issuerCertificate; + } + + public X509Certificate getValue() { + + return issuerCertificate; + } + + public String getKey() { + + return serialNumber; + } + + public boolean isValid() { + + // Will be always return true since we only set defined data. + return true; + } + + public long getTimeStamp() { + + return timeStamp; + } + + /** + * Used by cacheManager to remove invalid entries. + */ + public void removeThisCacheValue() { + + removeCacheValue(serialNumber); + } + + public void updateCacheWithNewValue() { + // No implementation needed since there are no scenarios of the cache value being invalid. + } + } +} diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/crl/CRLCache.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/crl/CRLCache.java index 11db425524..2597960102 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/crl/CRLCache.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/crl/CRLCache.java @@ -50,12 +50,13 @@ public class CRLCache implements ManageableCache { private CRLCache() { } - public static CRLCache getCache() { + public static CRLCache getCache(int cacheSize, int cacheDelayMins) { //Double checked locking if (cache == null) { synchronized (CRLCache.class) { if (cache == null) { cache = new CRLCache(); + cacheManager = new CacheManager(cache, cacheSize, cacheDelayMins); } } } diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/ocsp/OCSPCache.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/ocsp/OCSPCache.java index b01d509867..cf904cd3f4 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/ocsp/OCSPCache.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/ocsp/OCSPCache.java @@ -22,6 +22,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.synapse.commons.jmx.MBeanRegistrar; import org.apache.synapse.transport.certificatevalidation.CertificateVerificationException; +import org.apache.synapse.transport.certificatevalidation.Constants; import org.apache.synapse.transport.certificatevalidation.cache.CacheController; import org.apache.synapse.transport.certificatevalidation.cache.CacheManager; import org.apache.synapse.transport.certificatevalidation.cache.ManageableCache; @@ -51,12 +52,14 @@ public class OCSPCache implements ManageableCache { private OCSPCache() {} - public static OCSPCache getCache() { + public static OCSPCache getCache(int cacheSize, int cacheDelayMins) { //Double checked locking if (cache == null) { synchronized (OCSPCache.class) { - if (cache == null) + if (cache == null) { cache = new OCSPCache(); + cacheManager = new CacheManager(cache, cacheSize, cacheDelayMins); + } } } return cache; @@ -116,7 +119,7 @@ private synchronized void replaceNewCacheValue(OCSPCacheValue cacheValue){ try { String serviceUrl = cacheValue.serviceUrl; OCSPReq request = cacheValue.request; - OCSPResp response= ocspVerifier.getOCSPResponce(serviceUrl, request); + OCSPResp response= ocspVerifier.getOCSPResponse(serviceUrl, request); if (OCSPResponseStatus.SUCCESSFUL != response.getStatus()) throw new CertificateVerificationException("OCSP response status not SUCCESSFUL"); diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/ocsp/OCSPVerifier.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/ocsp/OCSPVerifier.java index d9a3a11039..5744365730 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/ocsp/OCSPVerifier.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/ocsp/OCSPVerifier.java @@ -20,20 +20,42 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.bouncycastle.asn1.*; +import org.apache.http.HttpResponse; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.synapse.transport.certificatevalidation.CertificateVerificationException; +import org.apache.synapse.transport.certificatevalidation.Constants; +import org.apache.synapse.transport.certificatevalidation.RevocationStatus; +import org.apache.synapse.transport.certificatevalidation.RevocationVerifier; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; import org.bouncycastle.asn1.ocsp.OCSPResponseStatus; -import org.bouncycastle.asn1.x509.*; +import org.bouncycastle.asn1.x509.AccessDescription; +import org.bouncycastle.asn1.x509.AuthorityInformationAccess; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.Extensions; +import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.ocsp.*; -import org.apache.synapse.transport.certificatevalidation.*; +import org.bouncycastle.cert.ocsp.BasicOCSPResp; +import org.bouncycastle.cert.ocsp.CertificateID; +import org.bouncycastle.cert.ocsp.CertificateStatus; +import org.bouncycastle.cert.ocsp.OCSPReq; +import org.bouncycastle.cert.ocsp.OCSPReqBuilder; +import org.bouncycastle.cert.ocsp.OCSPResp; +import org.bouncycastle.cert.ocsp.SingleResp; import org.bouncycastle.operator.DigestCalculatorProvider; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; import java.math.BigInteger; -import java.net.HttpURLConnection; -import java.net.URL; import java.security.Provider; import java.security.Security; import java.security.cert.X509Certificate; @@ -52,6 +74,12 @@ public OCSPVerifier(OCSPCache cache) { this.cache = cache; } + public static final String CONTENT_TYPE = "Content-Type"; + public static final String JSON_TYPE ="application/json"; + public static final String ACCEPT_TYPE = "Accept"; + public static final String OCSP_REQUEST_TYPE = "application/ocsp-request"; + public static final String OCSP_RESPONSE_TYPE = "application/ocsp-response"; + /** * Gets the revocation status (Good, Revoked or Unknown) of the given peer certificate. * @@ -83,7 +111,7 @@ public RevocationStatus checkRevocationStatus(X509Certificate peerCert, X509Cert SingleResp[] responses; try { - OCSPResp ocspResponse = getOCSPResponce(serviceUrl, request); + OCSPResp ocspResponse = getOCSPResponse(serviceUrl, request); if (OCSPResponseStatus.SUCCESSFUL != ocspResponse.getStatus()) { continue; // Server didn't give the response right. } @@ -128,37 +156,34 @@ private RevocationStatus getRevocationStatus(SingleResp resp) throws Certificate * @throws CertificateVerificationException * */ - protected OCSPResp getOCSPResponce(String serviceUrl, OCSPReq request) throws CertificateVerificationException { + protected OCSPResp getOCSPResponse(String serviceUrl, OCSPReq request) throws CertificateVerificationException { - try { - //Todo: Use http client. - byte[] array = request.getEncoded(); - if (serviceUrl.startsWith("http")) { - HttpURLConnection con; - URL url = new URL(serviceUrl); - con = (HttpURLConnection) url.openConnection(); - con.setRequestProperty("Content-Type", "application/ocsp-request"); - con.setRequestProperty("Accept", "application/ocsp-response"); - con.setDoOutput(true); - OutputStream out = con.getOutputStream(); - DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(out)); - dataOut.write(array); - - dataOut.flush(); - dataOut.close(); - - //Check errors in response: - if (con.getResponseCode() / 100 != 2) { - throw new CertificateVerificationException("Error getting ocsp response." + - "Response code is " + con.getResponseCode()); - } + if (log.isDebugEnabled()) { + log.debug("Initiating HTTP request to URL: " + serviceUrl + " to get the OCSP response"); + } + + try (CloseableHttpClient client = HttpClientBuilder.create().build()) { + HttpPost httpPost = new HttpPost(serviceUrl); + + // adding request timeout configurations + if (httpPost.getConfig() == null) { + httpPost.setConfig(RequestConfig.custom().build()); + } - //Get Response - InputStream in = (InputStream) con.getContent(); - return new OCSPResp(in); - } else { - throw new CertificateVerificationException("Only http is supported for ocsp calls"); + httpPost.addHeader(CONTENT_TYPE, OCSP_REQUEST_TYPE); + httpPost.addHeader(ACCEPT_TYPE, OCSP_RESPONSE_TYPE); + httpPost.setEntity(new ByteArrayEntity(request.getEncoded(), ContentType.create(JSON_TYPE))); + HttpResponse httpResponse = client.execute(httpPost); + + //Check errors in response, if response status code is not 200 (success) range, throws exception + // eg: if response code is 200 (success) or 201 (accepted) return true, + // if response code is 404 (not found) or 500 throw exception + if (httpResponse.getStatusLine().getStatusCode() / 100 != 2) { + throw new CertificateVerificationException("Error getting ocsp response." + + "Response code is " + httpResponse.getStatusLine().getStatusCode()); } + InputStream in = httpResponse.getEntity().getContent(); + return new OCSPResp(in); } catch (IOException e) { throw new CertificateVerificationException("Cannot get ocspResponse from url: " + serviceUrl, e); } diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/pathvalidation/CertificatePathValidator.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/pathvalidation/CertificatePathValidator.java index b041a7314d..b6335f88a4 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/pathvalidation/CertificatePathValidator.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/certificatevalidation/pathvalidation/CertificatePathValidator.java @@ -22,6 +22,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.synapse.transport.certificatevalidation.*; +import java.lang.reflect.InvocationTargetException; import java.security.*; import java.security.cert.X509Certificate; import java.util.List; diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/http/conn/ClientSSLSetupHandler.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/http/conn/ClientSSLSetupHandler.java index 24977a7115..e5c94d91f6 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/http/conn/ClientSSLSetupHandler.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/http/conn/ClientSSLSetupHandler.java @@ -33,7 +33,7 @@ import org.apache.http.nio.reactor.IOSession; import org.apache.http.nio.reactor.ssl.SSLSetupHandler; import org.apache.synapse.transport.certificatevalidation.CertificateVerificationException; -import org.apache.synapse.transport.certificatevalidation.RevocationVerificationManager; +import org.apache.synapse.transport.certificatevalidation.CertificateVerificationManager; public class ClientSSLSetupHandler implements SSLSetupHandler { @@ -139,10 +139,10 @@ public void verify( }; private final X509HostnameVerifier hostnameVerifier; - private final RevocationVerificationManager verificationManager; + private final CertificateVerificationManager verificationManager; public ClientSSLSetupHandler(final X509HostnameVerifier hostnameVerifier, - final RevocationVerificationManager verificationManager) { + final CertificateVerificationManager verificationManager) { this.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier : DEFAULT; this.verificationManager = verificationManager; } @@ -184,7 +184,7 @@ public void verify(IOSession iosession, SSLSession sslsession) throws SSLExcepti if (verificationManager!=null) { try { - verificationManager.verifyRevocationStatus(sslsession.getPeerCertificateChain()); + verificationManager.verifyCertificateValidity(sslsession.getPeerCertificateChain()); } catch (CertificateVerificationException e) { throw new SSLException("Certificate Chain Validation failed for host : " + address, e); } diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/http/conn/ServerSSLSetupHandler.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/http/conn/ServerSSLSetupHandler.java index a4bc324ce1..bb4e7c08d6 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/http/conn/ServerSSLSetupHandler.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/http/conn/ServerSSLSetupHandler.java @@ -21,11 +21,12 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; +import javax.security.cert.X509Certificate; import org.apache.http.nio.reactor.IOSession; import org.apache.http.nio.reactor.ssl.SSLSetupHandler; import org.apache.synapse.transport.certificatevalidation.CertificateVerificationException; -import org.apache.synapse.transport.certificatevalidation.RevocationVerificationManager; +import org.apache.synapse.transport.certificatevalidation.CertificateVerificationManager; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -36,12 +37,12 @@ public class ServerSSLSetupHandler implements SSLSetupHandler { private final SSLClientAuth clientAuth; /** Enabled SSL handshake protocols (e.g. SSLv3, TLSv1) */ private final String[] httpsProtocols; - private RevocationVerificationManager verificationManager; + private CertificateVerificationManager verificationManager; /** Ciphers enabled in axis2.xml, enabled all if null*/ private final String[] preferredCiphers; public ServerSSLSetupHandler(final SSLClientAuth clientAuth, final String[] httpsProtocols, - final RevocationVerificationManager verificationManager, final String[] preferredCiphers) { + final CertificateVerificationManager verificationManager, final String[] preferredCiphers) { this.clientAuth = clientAuth; this.httpsProtocols = httpsProtocols; this.verificationManager = verificationManager; @@ -78,7 +79,7 @@ public void verify( if (verificationManager != null) { try { - verificationManager.verifyRevocationStatus(sslsession.getPeerCertificateChain()); + verificationManager.verifyCertificateValidity(sslsession.getPeerCertificateChain()); } catch (CertificateVerificationException e) { SocketAddress remoteAddress = iosession.getRemoteAddress(); String address; diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/ClientConnFactoryBuilder.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/ClientConnFactoryBuilder.java index bf504b9999..5495ae9fc2 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/ClientConnFactoryBuilder.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/ClientConnFactoryBuilder.java @@ -19,27 +19,6 @@ package org.apache.synapse.transport.nhttp.config; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.xml.namespace.QName; -import javax.xml.stream.XMLStreamException; - -import org.apache.axiom.om.OMAttribute; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.impl.builder.StAXOMBuilder; import org.apache.axis2.AxisFault; @@ -52,8 +31,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.http.conn.ssl.X509HostnameVerifier; import org.apache.http.params.HttpParams; -import org.apache.synapse.commons.crypto.CryptoConstants; -import org.apache.synapse.transport.certificatevalidation.RevocationVerificationManager; +import org.apache.synapse.transport.certificatevalidation.CertificateVerificationManager; import org.apache.synapse.transport.exceptions.InvalidConfigurationException; import org.apache.synapse.transport.http.conn.ClientConnFactory; import org.apache.synapse.transport.http.conn.ClientSSLSetupHandler; @@ -63,8 +41,25 @@ import org.apache.synapse.transport.nhttp.util.SecureVaultValueReader; import org.wso2.securevault.SecretResolver; import org.wso2.securevault.SecretResolverFactory; -import org.wso2.securevault.SecureVaultException; -import org.wso2.securevault.commons.MiscellaneousUtil; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; public class ClientConnFactoryBuilder { @@ -76,6 +71,7 @@ public class ClientConnFactoryBuilder { private SSLContextDetails ssl = null; private Map sslByHostMap = null; private ConfigurationContext configurationContext; + TrustStoreHolder trustStoreHolder = TrustStoreHolder.getInstance(); public ClientConnFactoryBuilder(final TransportOutDescription transportOut, ConfigurationContext configurationContext) { this(transportOut); @@ -87,7 +83,7 @@ public ClientConnFactoryBuilder(final TransportOutDescription transportOut) { this.transportOut = transportOut; this.name = transportOut.getName().toUpperCase(Locale.US); } - + public ClientConnFactoryBuilder parseSSL() throws AxisFault { Parameter keyParam = transportOut.getParameter("keystore"); Parameter trustParam = transportOut.getParameter("truststore"); @@ -133,7 +129,7 @@ public ClientConnFactoryBuilder parseSSL() throws AxisFault { final Parameter cvp = transportOut.getParameter("CertificateRevocationVerifier"); final String cvEnable = cvp != null ? cvp.getParameterElement().getAttribute(new QName("enable")).getAttributeValue() : null; - RevocationVerificationManager revocationVerifier = null; + CertificateVerificationManager certificateVerifier = null; if ("true".equalsIgnoreCase(cvEnable)) { String cacheSizeString = cvp.getParameterElement().getFirstChildWithName(new QName("CacheSize")).getText(); @@ -146,7 +142,7 @@ public ClientConnFactoryBuilder parseSSL() throws AxisFault { cacheDelay = new Integer(cacheDelayString); } catch (NumberFormatException e) { } - revocationVerifier = new RevocationVerificationManager(cacheSize, cacheDelay); + certificateVerifier = new CertificateVerificationManager(cacheSize, cacheDelay); } // Process HttpProtocols @@ -167,7 +163,7 @@ public ClientConnFactoryBuilder parseSSL() throws AxisFault { } // Initiated separately to cater setting https protocols - ClientSSLSetupHandler clientSSLSetupHandler = new ClientSSLSetupHandler(hostnameVerifier, revocationVerifier); + ClientSSLSetupHandler clientSSLSetupHandler = new ClientSSLSetupHandler(hostnameVerifier, certificateVerifier); if (null != httpsProtocols) { clientSSLSetupHandler.setHttpsProtocols(httpsProtocols); @@ -353,28 +349,37 @@ private SSLContext createSSLContext(OMElement keyStoreElt, OMElement trustStoreE log.warn(name + " Ignoring novalidatecert parameter since a truststore has been specified"); } - String location = trustStoreElt.getFirstChildWithName(new QName("Location")).getText(); - String type = trustStoreElt.getFirstChildWithName(new QName("Type")).getText(); - OMElement passwordElement = trustStoreElt.getFirstChildWithName(new QName("Password")); - if (passwordElement == null) { - throw new AxisFault("Cannot proceed because Password element is missing in TrustStore"); - } - String storePassword = SecureVaultValueReader.getSecureVaultValue(resolver, passwordElement); - FileInputStream fis = null; + String location = trustStoreElt.getFirstChildWithName(new QName("Location")).getText();; + KeyStore trustStore; + try { - KeyStore trustStore = KeyStore.getInstance(type); - fis = new FileInputStream(location); - if (log.isDebugEnabled()) { - log.debug(name + " Loading Trust Keystore from : " + location); + if (trustStoreHolder.getClientTrustStore() == null) { + String type = trustStoreElt.getFirstChildWithName(new QName("Type")).getText(); + OMElement passwordElement = trustStoreElt.getFirstChildWithName(new QName("Password")); + + if (passwordElement == null) { + throw new AxisFault("Cannot proceed because Password element is missing in TrustStore"); + } + + String storePassword = SecureVaultValueReader.getSecureVaultValue(resolver, passwordElement); + trustStore = KeyStore.getInstance(type); + fis = new FileInputStream(location); + + if (log.isDebugEnabled()) { + log.debug(name + " Loading Trust Keystore from : " + location); + } + + trustStore.load(fis, storePassword.toCharArray()); + trustStoreHolder.setClientTrustStore(trustStore); + } else { + trustStore = trustStoreHolder.getClientTrustStore(); } - trustStore.load(fis, storePassword.toCharArray()); TrustManagerFactory trustManagerfactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); trustManagerfactory.init(trustStore); trustManagers = trustManagerfactory.getTrustManagers(); - } catch (GeneralSecurityException gse) { log.error(name + " Error loading Key store : " + location, gse); throw new AxisFault("Error loading Key store : " + location, gse); @@ -424,9 +429,9 @@ private SSLContext createSSLContext(OMElement keyStoreElt, OMElement trustStoreE keyStoreElt.getFirstChildWithName(new QName("Password"))); String keyPassword = SecureVaultValueReader.getSecureVaultValue(secretResolver, keyStoreElt.getFirstChildWithName(new QName("KeyPassword"))); - - try (FileInputStream fis = new FileInputStream(location)) { - KeyStore keyStore = KeyStore.getInstance(type); + + try (FileInputStream fis = new FileInputStream(location)) { + KeyStore keyStore = KeyStore.getInstance(type); if (log.isDebugEnabled()) { log.debug(name + " Loading Identity Keystore from : " + location); } @@ -443,7 +448,7 @@ private SSLContext createSSLContext(OMElement keyStoreElt, OMElement trustStoreE } catch (IOException ioe) { log.error(name + " Error opening Keystore : " + location, ioe); throw new AxisFault("Error opening Keystore : " + location, ioe); - } + } } if (trustStoreElt != null) { @@ -451,31 +456,35 @@ private SSLContext createSSLContext(OMElement keyStoreElt, OMElement trustStoreE log.warn(name + " Ignoring novalidatecert parameter since a truststore has been specified"); } + KeyStore trustStore; String location = trustStoreElt.getFirstChildWithName(new QName("Location")).getText(); - String type = trustStoreElt.getFirstChildWithName(new QName("Type")).getText(); - String storePassword = SecureVaultValueReader - .getSecureVaultValue(secretResolver, trustStoreElt.getFirstChildWithName(new QName("Password"))); - + try (FileInputStream fis = new FileInputStream(location)) { - KeyStore trustStore = KeyStore.getInstance(type); - - if (log.isDebugEnabled()) { - log.debug(name + " Loading Trust Keystore from : " + location); + if (trustStoreHolder.getClientTrustStore() == null) { + String type = trustStoreElt.getFirstChildWithName(new QName("Type")).getText(); + String storePassword = SecureVaultValueReader.getSecureVaultValue(secretResolver, + trustStoreElt.getFirstChildWithName(new QName("Password"))); + trustStore = KeyStore.getInstance(type); + if (log.isDebugEnabled()) { + log.debug(name + " Loading Trust Keystore from : " + location); + } + trustStore.load(fis, storePassword.toCharArray()); + trustStoreHolder.setClientTrustStore(trustStore); + } else { + trustStore = trustStoreHolder.getClientTrustStore(); } - trustStore.load(fis, storePassword.toCharArray()); TrustManagerFactory trustManagerfactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); trustManagerfactory.init(trustStore); trustManagers = trustManagerfactory.getTrustManagers(); - } catch (GeneralSecurityException gse) { log.error(name + " Error loading Key store : " + location, gse); throw new AxisFault("Error loading Key store : " + location, gse); } catch (IOException ioe) { log.error(name + " Error opening Key store : " + location, ioe); throw new AxisFault("Error opening Key store : " + location, ioe); - } + } } else if (novalidatecert) { if (log.isWarnEnabled()) { log.warn(name + " Server certificate validation (trust) has been disabled. " + diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/ServerConnFactoryBuilder.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/ServerConnFactoryBuilder.java index 817c11e99b..b9b4b0018e 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/ServerConnFactoryBuilder.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/ServerConnFactoryBuilder.java @@ -26,11 +26,12 @@ import org.apache.axis2.description.Parameter; import org.apache.axis2.description.TransportInDescription; import org.apache.axis2.transport.base.ParamUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpHost; import org.apache.http.params.HttpParams; -import org.apache.synapse.transport.certificatevalidation.RevocationVerificationManager; +import org.apache.synapse.transport.certificatevalidation.CertificateVerificationManager; import org.apache.synapse.transport.http.conn.SSLClientAuth; import org.apache.synapse.transport.http.conn.SSLContextDetails; import org.apache.synapse.transport.http.conn.ServerConnFactory; @@ -40,6 +41,13 @@ import org.wso2.securevault.SecretResolver; import org.wso2.securevault.SecretResolverFactory; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509KeyManager; +import javax.xml.namespace.QName; import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; @@ -54,14 +62,6 @@ import java.util.Locale; import java.util.Map; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509KeyManager; -import javax.xml.namespace.QName; - public class ServerConnFactoryBuilder { private final Log log = LogFactory.getLog(ServerConnFactoryBuilder.class); @@ -73,6 +73,7 @@ public class ServerConnFactoryBuilder { protected SSLContextDetails ssl; private Map sslByIPMap = null; private ConfigurationContext configurationContext; + CertificateVerificationManager certificateVerifier = null; public ServerConnFactoryBuilder(final TransportInDescription transportIn, final HttpHost host, ConfigurationContext configurationContext) { @@ -89,13 +90,13 @@ public ServerConnFactoryBuilder(final TransportInDescription transportIn, final } protected SSLContextDetails createSSLContext( - final OMElement keyStoreEl, - final OMElement trustStoreEl, - final OMElement cientAuthEl, - final OMElement httpsProtocolsEl, - final OMElement preferredCiphersEl, - final RevocationVerificationManager verificationManager, - final String sslProtocol) throws AxisFault { + final OMElement keyStoreEl, + final OMElement trustStoreEl, + final OMElement cientAuthEl, + final OMElement httpsProtocolsEl, + final OMElement preferredCiphersEl, + final CertificateVerificationManager verificationManager, + final String sslProtocol) throws AxisFault { SecretResolver secretResolver; if (configurationContext != null && configurationContext.getAxisConfiguration() != null) { @@ -114,7 +115,7 @@ protected SSLContextDetails createSSLContext( final OMElement cientAuthEl, final OMElement httpsProtocolsEl, final OMElement preferredCiphersEl, - final RevocationVerificationManager verificationManager, + final CertificateVerificationManager verificationManager, final String sslProtocol, final SecretResolver secretResolver) throws AxisFault { KeyManager[] keymanagers = null; @@ -182,25 +183,38 @@ protected SSLContextDetails createSSLContext( } if (trustStoreEl != null) { - String location = getValueOfElementWithLocalName(trustStoreEl, "Location"); - String type = getValueOfElementWithLocalName(trustStoreEl, "Type"); - OMElement storePasswordEl = trustStoreEl.getFirstChildWithName(new QName("Password")); - if (storePasswordEl == null) { - throw new AxisFault("Cannot proceed because Password element is missing in TrustStore"); - } - String storePassword = SecureVaultValueReader.getSecureVaultValue(secretResolver, storePasswordEl); + TrustStoreHolder trustStoreHolder = TrustStoreHolder.getInstance(); FileInputStream fis = null; + String location = getValueOfElementWithLocalName(trustStoreEl, "Location"); + KeyStore trustStore; + try { - KeyStore trustStore = KeyStore.getInstance(type); - fis = new FileInputStream(location); - if (log.isDebugEnabled()) { - log.debug(name + " Loading Trust Keystore from : " + location); + if (trustStoreHolder.getClientTrustStore() == null) { + + String type = getValueOfElementWithLocalName(trustStoreEl, "Type"); + OMElement storePasswordEl = trustStoreEl.getFirstChildWithName(new QName("Password")); + + if (storePasswordEl == null) { + throw new AxisFault("Cannot proceed because Password element is missing in TrustStore"); + } + + String storePassword = SecureVaultValueReader.getSecureVaultValue(secretResolver, storePasswordEl); + trustStore = KeyStore.getInstance(type); + fis = new FileInputStream(location); + + if (log.isDebugEnabled()) { + log.debug(name + " Loading Trust Keystore from : " + location); + } + + trustStore.load(fis, storePassword.toCharArray()); + trustStoreHolder.setClientTrustStore(trustStore); + } else { + trustStore = trustStoreHolder.getClientTrustStore(); } - trustStore.load(fis, storePassword.toCharArray()); TrustManagerFactory trustManagerfactory = TrustManagerFactory.getInstance( - TrustManagerFactory.getDefaultAlgorithm()); + TrustManagerFactory.getDefaultAlgorithm()); trustManagerfactory.init(trustStore); trustManagers = trustManagerfactory.getTrustManagers(); @@ -294,7 +308,6 @@ public ServerConnFactoryBuilder parseSSL() throws AxisFault { final Parameter cvp = transportIn.getParameter("CertificateRevocationVerifier"); final String cvEnable = cvp != null ? cvp.getParameterElement().getAttribute(new QName("enable")).getAttributeValue() : null; - RevocationVerificationManager revocationVerifier = null; if ("true".equalsIgnoreCase(cvEnable)) { String cacheSizeString = cvp.getParameterElement().getFirstChildWithName(new QName("CacheSize")).getText(); @@ -305,12 +318,33 @@ public ServerConnFactoryBuilder parseSSL() throws AxisFault { cacheSize = new Integer(cacheSizeString); cacheDelay = new Integer(cacheDelayString); } - catch (NumberFormatException e) {} - revocationVerifier = new RevocationVerificationManager(cacheSize, cacheDelay); + catch (NumberFormatException e) { + throw new AxisFault("Cache size or Cache delay values are malformed", e); + } + + // Checking whether the full certificate chain validation is enabled or not. + boolean isFullCertChainValidationEnabled = true; + boolean isCertExpiryValidationEnabled = false; + OMElement fullCertChainValidationConfig = cvp.getParameterElement() + .getFirstChildWithName(new QName("FullChainValidation")); + OMElement certExpiryValidationConfig = cvp.getParameterElement() + .getFirstChildWithName(new QName("ExpiryValidation")); + + if (fullCertChainValidationConfig != null + && StringUtils.equals("false", fullCertChainValidationConfig.getText())) { + isFullCertChainValidationEnabled = false; + } + + if (certExpiryValidationConfig != null && StringUtils.equals("true", certExpiryValidationConfig.getText())) { + isCertExpiryValidationEnabled = true; + } + + certificateVerifier = new CertificateVerificationManager(cacheSize, cacheDelay, + isFullCertChainValidationEnabled, isCertExpiryValidationEnabled); } ssl = createSSLContext(keyStoreEl, trustStoreEl, clientAuthEl, httpsProtocolsEl, preferredCiphersEl, - revocationVerifier, sslProtocol); + certificateVerifier, sslProtocol); return this; } @@ -324,6 +358,7 @@ public ServerConnFactoryBuilder parseMultiProfileSSL() throws AxisFault { OMElement profilesEl = profileParam.getParameterElement(); SecretResolver secretResolver = SecretResolverFactory.create(profilesEl, true); Iterator profiles = profilesEl.getChildrenWithName(new QName("profile")); + while (profiles.hasNext()) { OMElement profileEl = (OMElement) profiles.next(); OMElement bindAddressEl = profileEl.getFirstChildWithName(new QName("bindAddress")); @@ -341,8 +376,63 @@ public ServerConnFactoryBuilder parseMultiProfileSSL() throws AxisFault { OMElement preferredCiphersEl = profileEl.getFirstChildWithName(new QName(NhttpConstants.PREFERRED_CIPHERS)); final Parameter sslpParameter = transportIn.getParameter("SSLProtocol"); final String sslProtocol = sslpParameter != null ? sslpParameter.getValue().toString() : "TLS"; + + /* If multi SSL profiles are configured, checking whether the certificate revocation verifier is + configured and full certificate chain validation is enabled or not. */ + if (profileEl.getFirstChildWithName(new QName("CertificateRevocationVerifier")) != null) { + + Integer cacheSize = null; + Integer cacheDelay = null; + + OMElement revocationVerifierConfig = profileEl + .getFirstChildWithName(new QName("CertificateRevocationVerifier")); + OMElement revocationEnabled = revocationVerifierConfig + .getFirstChildWithName(new QName("Enable")); + + if (revocationEnabled != null && "true".equals(revocationEnabled.getText())) { + String cacheSizeString = revocationVerifierConfig + .getFirstChildWithName(new QName("CacheSize")).getText(); + String cacheDelayString = revocationVerifierConfig + .getFirstChildWithName(new QName("CacheDelay")).getText(); + + try { + cacheSize = new Integer(cacheSizeString); + cacheDelay = new Integer(cacheDelayString); + } catch (NumberFormatException e) { + throw new AxisFault("Cache size or Cache delay values are malformed", e); + } + } + + boolean isFullCertChainValidationEnabled = true; + boolean isCertExpiryValidationEnabled = false; + + OMElement fullCertChainValidationConfig = revocationVerifierConfig + .getFirstChildWithName(new QName("FullChainValidation")); + + OMElement certExpiryValidationConfig = revocationVerifierConfig + .getFirstChildWithName(new QName("ExpiryValidation")); + + if (fullCertChainValidationConfig != null + && StringUtils.equals("false", fullCertChainValidationConfig.getText())) { + isFullCertChainValidationEnabled = false; + } + + if (certExpiryValidationConfig != null + && StringUtils.equals("true", certExpiryValidationConfig.getText())) { + isCertExpiryValidationEnabled = true; + } + + if (fullCertChainValidationConfig != null + && StringUtils.equals("false", fullCertChainValidationConfig.getText())) { + isFullCertChainValidationEnabled = false; + } + + certificateVerifier = new CertificateVerificationManager(cacheSize, cacheDelay, + isFullCertChainValidationEnabled, isCertExpiryValidationEnabled); + } + SSLContextDetails ssl = createSSLContext(keyStoreEl, trustStoreEl, clientAuthEl, httpsProtocolsEl, - preferredCiphersEl, null, sslProtocol, secretResolver); + preferredCiphersEl, certificateVerifier, sslProtocol, secretResolver); if (sslByIPMap == null) { sslByIPMap = new HashMap(); } diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/TrustStoreHolder.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/TrustStoreHolder.java new file mode 100644 index 0000000000..697dd6eba6 --- /dev/null +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/nhttp/config/TrustStoreHolder.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.synapse.transport.nhttp.config; + +import java.security.KeyStore; + +/** + * A data holder class to store the client trust store. + */ +public class TrustStoreHolder { + + private static volatile TrustStoreHolder instance; + private KeyStore clientTrustStore; + + private TrustStoreHolder() {} + + public static TrustStoreHolder getInstance() { + + if (instance == null) { + synchronized (TrustStoreHolder.class) { + if (instance == null) { + instance = new TrustStoreHolder(); + } + } + } + return instance; + } + + public KeyStore getClientTrustStore() { + + return clientTrustStore; + } + + public void setClientTrustStore(KeyStore clientTrustStore) { + + this.clientTrustStore = clientTrustStore; + } + + /** + * Reset the instance. + */ + public static void resetInstance() { + + instance = null; + } +} diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpListener.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpListener.java index 1d4ddf2a6a..3e7f92c6d6 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpListener.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpListener.java @@ -47,8 +47,6 @@ import org.apache.axis2.transport.TransportListener; import org.apache.axis2.transport.base.BaseConstants; import org.apache.axis2.transport.base.BaseUtils; -import org.apache.axis2.transport.base.ManagementSupport; -import org.apache.axis2.transport.base.TransportMBeanSupport; import org.apache.axis2.transport.base.threads.NativeThreadFactory; import org.apache.axis2.transport.base.threads.WorkerPool; import org.apache.axis2.transport.base.tracker.AxisServiceFilter; @@ -62,8 +60,10 @@ import org.apache.http.nio.reactor.IOReactorException; import org.apache.http.nio.reactor.IOReactorExceptionHandler; import org.apache.http.params.HttpConnectionParams; +import org.apache.synapse.transport.certificatevalidation.cache.CertCache; import org.apache.synapse.transport.http.conn.Scheme; import org.apache.synapse.transport.http.conn.ServerConnFactory; +import org.apache.synapse.transport.nhttp.config.TrustStoreHolder; import org.apache.synapse.transport.nhttp.config.ServerConnFactoryBuilder; import org.apache.synapse.transport.passthru.config.PassThroughConfiguration; import org.apache.synapse.transport.passthru.config.SourceConfiguration; @@ -683,5 +683,4 @@ public void reloadDynamicSSLConfig(TransportInDescription transportInDescription this.reloadSpecificEndPoints(transportInDescription); } } - } diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpMultiSSLListener.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpMultiSSLListener.java index 208b364178..406ae5a53b 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpMultiSSLListener.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpMultiSSLListener.java @@ -5,10 +5,12 @@ import org.apache.axis2.description.ParameterInclude; import org.apache.axis2.description.TransportInDescription; import org.apache.http.HttpHost; +import org.apache.synapse.transport.certificatevalidation.cache.CertCache; import org.apache.synapse.transport.http.conn.Scheme; import org.apache.synapse.transport.nhttp.config.ServerConnFactoryBuilder; import org.apache.synapse.transport.dynamicconfigurations.ListenerProfileReloader; import org.apache.synapse.transport.dynamicconfigurations.SSLProfileLoader; +import org.apache.synapse.transport.nhttp.config.TrustStoreHolder; public class PassThroughHttpMultiSSLListener extends PassThroughHttpListener implements SSLProfileLoader{ @@ -39,6 +41,8 @@ protected ServerConnFactoryBuilder initConnFactoryBuilder(final TransportInDescr * @throws AxisFault */ public void reloadConfig(ParameterInclude transport) throws AxisFault { + CertCache.resetCache(); + TrustStoreHolder.resetInstance(); reloadDynamicSSLConfig((TransportInDescription) transport); } diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSender.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSender.java index 3af961c03f..b977eb30a7 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSender.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/PassThroughHttpSender.java @@ -47,12 +47,14 @@ import org.apache.http.nio.reactor.IOReactorExceptionHandler; import org.apache.http.protocol.HTTP; import org.apache.synapse.commons.CorrelationConstants; +import org.apache.synapse.transport.certificatevalidation.cache.CertCache; import org.apache.synapse.transport.exceptions.InvalidConfigurationException; import org.apache.synapse.transport.http.conn.ClientConnFactory; import org.apache.synapse.transport.http.conn.ProxyConfig; import org.apache.synapse.transport.http.conn.Scheme; import org.apache.synapse.transport.nhttp.NhttpConstants; import org.apache.synapse.transport.nhttp.config.ClientConnFactoryBuilder; +import org.apache.synapse.transport.nhttp.config.TrustStoreHolder; import org.apache.synapse.transport.nhttp.config.ProxyConfigBuilder; import org.apache.synapse.transport.nhttp.util.MessageFormatterDecoratorFactory; import org.apache.synapse.transport.nhttp.util.NhttpUtil; @@ -557,7 +559,7 @@ public void submitResponse(MessageContext msgContext) ProtocolState state = SourceContext.getState(conn); if (state != null && state.compareTo(ProtocolState.REQUEST_DONE) <= 0) { // start sending the response if we - + boolean noEntityBodyResponse = false; if (noEntityBody != null && Boolean.TRUE == noEntityBody && pipe != null) { @@ -567,7 +569,7 @@ public void submitResponse(MessageContext msgContext) out.close(); noEntityBodyResponse = true; } - + if (!noEntityBodyResponse && msgContext.isPropertyTrue(PassThroughConstants.MESSAGE_BUILDER_INVOKED) && pipe != null) { OutputStream out = pipe.getOutputStream(); /*if (msgContext.isPropertyTrue(NhttpConstants.SC_ACCEPTED)) { diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/Pipe.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/Pipe.java index 73fe7ea168..a13403bd16 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/Pipe.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/Pipe.java @@ -672,7 +672,6 @@ public void write(final byte[] b, int off, int len) throws IOException { return; } else if (isProducerError()) { writeCondition.signalAll(); - buffer.clear(); return; } } else if (consumerIoControl instanceof NHttpClientConnection) { diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/SourceHandler.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/SourceHandler.java index 335ccb17de..bcd7242f8a 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/SourceHandler.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/SourceHandler.java @@ -28,6 +28,7 @@ import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.HttpVersion; +import org.apache.http.MethodNotSupportedException; import org.apache.http.message.BasicHttpResponse; import org.apache.http.nio.ContentDecoder; import org.apache.http.nio.ContentEncoder; @@ -895,42 +896,12 @@ public void exception(NHttpServerConnection conn, Exception ex) { isFault = true; SourceContext.updateState(conn, ProtocolState.CLOSED); sourceConfiguration.getSourceConnections().shutDownConnection(conn, true); + } else if (ex instanceof MethodNotSupportedException) { + isFault = generateHTTPErrorResponse(conn, ex, HttpVersion.HTTP_1_1, HttpStatus.SC_NOT_IMPLEMENTED, + "Not Implemented"); } else if (ex instanceof HttpException) { - log.error("HttpException occurred ", ex); - if (PassThroughCorrelationConfigDataHolder.isEnable()) { - logHttpRequestErrorInCorrelationLog(conn, "HTTP Exception"); - } - try { - if (conn.isResponseSubmitted()) { - sourceConfiguration.getSourceConnections().shutDownConnection(conn, true); - return; - } - HttpContext httpContext = conn.getContext(); - - HttpResponse response = new BasicHttpResponse( - HttpVersion.HTTP_1_1, HttpStatus.SC_BAD_REQUEST, "Bad request"); - response.setParams( - new DefaultedHttpParams(sourceConfiguration.getHttpParams(), - response.getParams())); - response.addHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE); - - // Pre-process HTTP request - httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn); - httpContext.setAttribute(ExecutionContext.HTTP_REQUEST, null); - httpContext.setAttribute(ExecutionContext.HTTP_RESPONSE, response); - - sourceConfiguration.getHttpProcessor().process(response, httpContext); - - conn.submitResponse(response); - SourceContext.updateState(conn, ProtocolState.CLOSED); - informWriterError(conn); - conn.close(); - } catch (Exception ex1) { - log.error(ex1.getMessage(), ex1); - SourceContext.updateState(conn, ProtocolState.CLOSED); - sourceConfiguration.getSourceConnections().shutDownConnection(conn, true); - isFault = true; - } + isFault = generateHTTPErrorResponse(conn, ex, HttpVersion.HTTP_1_1, HttpStatus.SC_BAD_REQUEST, + "Bad request"); } else { log.error("Unexpected error: " + ex.getMessage(), ex); SourceContext.updateState(conn, ProtocolState.CLOSED); @@ -943,6 +914,45 @@ public void exception(NHttpServerConnection conn, Exception ex) { } } + private boolean generateHTTPErrorResponse (NHttpServerConnection conn, Exception ex, HttpVersion version, + int statusCode, String reason) { + log.error("HttpException occurred ", ex); + boolean isFault = false; + if (PassThroughCorrelationConfigDataHolder.isEnable()) { + logHttpRequestErrorInCorrelationLog(conn, "HTTP Exception"); + } + try { + if (conn.isResponseSubmitted()) { + sourceConfiguration.getSourceConnections().shutDownConnection(conn, true); + return false; + } + HttpContext httpContext = conn.getContext(); + + HttpResponse response = new BasicHttpResponse(version, statusCode, reason); + response.setParams( + new DefaultedHttpParams(sourceConfiguration.getHttpParams(), response.getParams())); + response.addHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE); + + // Pre-process HTTP request + httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn); + httpContext.setAttribute(ExecutionContext.HTTP_REQUEST, null); + httpContext.setAttribute(ExecutionContext.HTTP_RESPONSE, response); + + sourceConfiguration.getHttpProcessor().process(response, httpContext); + + conn.submitResponse(response); + SourceContext.updateState(conn, ProtocolState.CLOSED); + informWriterError(conn); + conn.close(); + } catch (Exception ex1) { + log.error(ex1.getMessage(), ex1); + SourceContext.updateState(conn, ProtocolState.CLOSED); + sourceConfiguration.getSourceConnections().shutDownConnection(conn, true); + isFault = true; + } + return isFault; + } + private Map getLoggingInfo(NHttpServerConnection conn, ProtocolState state) { HashMap logDetails = new HashMap<>(); SourceContext sourceContext = SourceContext.get(conn); diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/core/ssl/SSLServerConnFactoryBuilder.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/core/ssl/SSLServerConnFactoryBuilder.java index 282900b956..8ea5433d4f 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/core/ssl/SSLServerConnFactoryBuilder.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/passthru/core/ssl/SSLServerConnFactoryBuilder.java @@ -19,12 +19,11 @@ import org.apache.axiom.om.OMElement; import org.apache.axis2.AxisFault; -import org.apache.axis2.description.Parameter; import org.apache.axis2.description.TransportInDescription; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpHost; -import org.apache.synapse.transport.certificatevalidation.RevocationVerificationManager; +import org.apache.synapse.transport.certificatevalidation.CertificateVerificationManager; import org.apache.synapse.transport.nhttp.config.ServerConnFactoryBuilder; import javax.xml.namespace.QName; @@ -46,7 +45,7 @@ public ServerConnFactoryBuilder parseSSL(OMElement keyStoreEl, OMElement trustSt AxisFault { final String cvEnable = cvp != null ? cvp.getAttribute(new QName("enable")).getAttributeValue() : null; - RevocationVerificationManager revocationVerifier = null; + CertificateVerificationManager revocationVerifier = null; if ("true".equalsIgnoreCase(cvEnable)) { Iterator iterator = cvp.getChildElements(); @@ -70,7 +69,7 @@ public ServerConnFactoryBuilder parseSSL(OMElement keyStoreEl, OMElement trustSt } catch (NumberFormatException e) { log.error("Please specify correct Integer numbers for CacheDelay and CacheSize"); } - revocationVerifier = new RevocationVerificationManager(cacheSize, cacheDelay); + revocationVerifier = new CertificateVerificationManager(cacheSize, cacheDelay); } ssl = createSSLContext(keyStoreEl, trustStoreEl, clientAuthEl, httpsProtocolsEl, preferredCiphers, revocationVerifier, diff --git a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/util/ConfigurationBuilderUtil.java b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/util/ConfigurationBuilderUtil.java index f5a81c1572..406a7a2d30 100644 --- a/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/util/ConfigurationBuilderUtil.java +++ b/modules/transports/core/nhttp/src/main/java/org/apache/synapse/transport/util/ConfigurationBuilderUtil.java @@ -170,5 +170,4 @@ public static String getStringProperty(String name, String def, Properties props return val == null ? def : val; } - } diff --git a/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/certificatevalidation/CRLVerifierTest.java b/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/certificatevalidation/CRLVerifierTest.java index 38d7964b8a..ec582f9dd7 100644 --- a/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/certificatevalidation/CRLVerifierTest.java +++ b/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/certificatevalidation/CRLVerifierTest.java @@ -88,8 +88,7 @@ public void testRevokedCertificate() throws Exception { //Create a crl with fakeRevokedCertificate marked as revoked. X509CRL x509CRL = createCRL(fakeCACert, caKeyPair.getPrivate(), revokedSerialNumber); - CRLCache cache = CRLCache.getCache(); - cache.init(5, 5); + CRLCache cache = CRLCache.getCache(5, 5); cache.setCacheValue(crlDistributionPointUrl, x509CRL); CRLVerifier crlVerifier = new CRLVerifier(cache); diff --git a/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/certificatevalidation/OCSPVerifierTest.java b/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/certificatevalidation/OCSPVerifierTest.java index 9c2299bb2b..9d4f0a01bd 100644 --- a/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/certificatevalidation/OCSPVerifierTest.java +++ b/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/certificatevalidation/OCSPVerifierTest.java @@ -107,8 +107,7 @@ public void testOCSPVerifier() throws Exception{ OCSPResp response = generateOCSPResponse(request, certificateHolder, caKeyPair.getPrivate(), caKeyPair.getPublic(), revokedID); SingleResp singleResp = ((BasicOCSPResp)response.getResponseObject()).getResponses()[0]; - OCSPCache cache = OCSPCache.getCache(); - cache.init(5,5); + OCSPCache cache = OCSPCache.getCache(5, 5); cache.setCacheValue(revokedSerialNumber,singleResp, request, null); OCSPVerifier ocspVerifier= new OCSPVerifier(cache); diff --git a/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/certificatevalidation/RevocationVerificationTest.java b/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/certificatevalidation/RevocationVerificationTest.java index 7f282dd86c..9adc063ea0 100644 --- a/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/certificatevalidation/RevocationVerificationTest.java +++ b/modules/transports/core/nhttp/src/test/java/org/apache/synapse/transport/certificatevalidation/RevocationVerificationTest.java @@ -116,8 +116,7 @@ public void testOCSPPathValidationWithFakeCerts() throws Exception { private void crlPathValidation(X509Certificate[] certChain) throws Exception { - CRLCache crlCache = CRLCache.getCache(); - crlCache.init(5, 5); + CRLCache crlCache = CRLCache.getCache(5, 5); RevocationVerifier verifier = new CRLVerifier(crlCache); CertificatePathValidator pathValidator = new CertificatePathValidator(certChain, verifier); pathValidator.validatePath(); @@ -125,8 +124,7 @@ private void crlPathValidation(X509Certificate[] certChain) throws Exception { private void ocspPathValidation(X509Certificate[] certChain) throws Exception { - OCSPCache ocspCache = OCSPCache.getCache(); - ocspCache.init(5, 5); + OCSPCache ocspCache = OCSPCache.getCache(5, 5); RevocationVerifier verifier = new OCSPVerifier(ocspCache); CertificatePathValidator pathValidator = new CertificatePathValidator(certChain, verifier); pathValidator.validatePath(); diff --git a/modules/transports/core/pipe/pom.xml b/modules/transports/core/pipe/pom.xml index 4f399083c6..646cbd743c 100644 --- a/modules/transports/core/pipe/pom.xml +++ b/modules/transports/core/pipe/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse synapse-transports - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/transports/core/vfs/pom.xml b/modules/transports/core/vfs/pom.xml index 30aab7c13a..ba4e8c278c 100644 --- a/modules/transports/core/vfs/pom.xml +++ b/modules/transports/core/vfs/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse synapse-transports - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/transports/core/vfs/repository/conf/vfs-move-failed-records.properties b/modules/transports/core/vfs/repository/conf/vfs-move-failed-records.properties new file mode 100644 index 0000000000..8f1ee4a76d --- /dev/null +++ b/modules/transports/core/vfs/repository/conf/vfs-move-failed-records.properties @@ -0,0 +1 @@ +t-0-1705986523002.txt 23/01/2024/ 10:38:43 \ No newline at end of file diff --git a/modules/transports/optional/fix/pom.xml b/modules/transports/optional/fix/pom.xml index ba0d6462df..0b1ca0e531 100644 --- a/modules/transports/optional/fix/pom.xml +++ b/modules/transports/optional/fix/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse synapse-transports - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/transports/pom.xml b/modules/transports/pom.xml index 74939da9d1..7779daced2 100644 --- a/modules/transports/pom.xml +++ b/modules/transports/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/war/pom.xml b/modules/war/pom.xml index 4c3ddb51a2..3245da206a 100644 --- a/modules/war/pom.xml +++ b/modules/war/pom.xml @@ -23,7 +23,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml diff --git a/modules/xar-maven-plugin/pom.xml b/modules/xar-maven-plugin/pom.xml index 041c9ea649..441411c9a6 100644 --- a/modules/xar-maven-plugin/pom.xml +++ b/modules/xar-maven-plugin/pom.xml @@ -22,7 +22,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT ../../pom.xml org.apache.synapse diff --git a/pom.xml b/pom.xml index efee8ef8b6..165a8ef696 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ org.apache.synapse Apache-Synapse - 4.0.0-wso2v72-SNAPSHOT + 4.0.0-wso2v79-SNAPSHOT Apache Synapse Apache Synapse pom @@ -1566,10 +1566,10 @@ 2.0.9 3.0.0 2.2.6.wso2v9 - 2.13.4 - 2.13.4.2 - 32.1.3-jre - 1.0.1 + 2.16.1 + 2.16.1 + 33.0.0-jre + 1.0.2 2.9.4.wso2v1 7.4.2.wso2v1 0.8.6 @@ -1585,11 +1585,11 @@ 5.9.1 1.5.2 1.17.0 - 4.1.100.Final - 6.3.49 + 4.1.104.Final + 6.3.50 1.1.1.wso2v3 5.8.0 - 1.11.0.wso2v5 + 1.11.0.wso2v6 4.9.3.wso2v2 2.8.0.wso2v2 2.16.3 diff --git a/repository/conf/sample/synapse_sample_63.xml b/repository/conf/sample/synapse_sample_63.xml index fbc676bfda..8e1d7d1e74 100644 --- a/repository/conf/sample/synapse_sample_63.xml +++ b/repository/conf/sample/synapse_sample_63.xml @@ -211,5 +211,33 @@ + + + + + + + + + + + my_client_id + my_client_secret + {$ctx:token_ep} + + + + + + + + + + + + + + +