diff --git a/components/org.wso2.carbon.identity.application.authenticator.oidc/pom.xml b/components/org.wso2.carbon.identity.application.authenticator.oidc/pom.xml
index aeb75b7e..7f184ef8 100644
--- a/components/org.wso2.carbon.identity.application.authenticator.oidc/pom.xml
+++ b/components/org.wso2.carbon.identity.application.authenticator.oidc/pom.xml
@@ -56,6 +56,10 @@
org.wso2.carbon.identity.framework
org.wso2.carbon.identity.application.authentication.framework
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.central.log.mgt
+
org.wso2.carbon.identity.inbound.auth.oauth2
org.wso2.carbon.identity.oauth.common
@@ -123,6 +127,11 @@
${carbon.identity.framework.version}
test
+
+ org.wso2.carbon.identity.organization.management.core
+ org.wso2.carbon.identity.organization.management.service
+ test
+
@@ -175,7 +184,9 @@
version="${carbon.identity.inbound.oauth.package.import.version.range}",
org.wso2.carbon.idp.mgt; version="${identity.framework.package.import.version.range}",
org.wso2.carbon.identity.application.common.util;
- version="${identity.framework.package.import.version.range}"
+ version="${identity.framework.package.import.version.range}",
+ org.wso2.carbon.identity.central.log.mgt.utils;
+ version="${identity.framework.package.import.version.range}",
!org.wso2.carbon.identity.application.authenticator.oidc.internal,
diff --git a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OIDCAuthenticatorConstants.java b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OIDCAuthenticatorConstants.java
index 81a0f440..817aecbd 100644
--- a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OIDCAuthenticatorConstants.java
+++ b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OIDCAuthenticatorConstants.java
@@ -135,4 +135,21 @@ private BackchannelLogout() {
public static final long DEFAULT_IAT_VALIDITY_PERIOD = 15000;
}
+
+ /**
+ * Constants related to log management.
+ */
+ public static class LogConstants {
+
+ public static final String OUTBOUND_AUTH_OIDC_SERVICE = "outbound-auth-oidc";
+
+ /**
+ * Define action IDs for diagnostic logs.
+ */
+ public static class ActionIDs {
+
+ public static final String PROCESS_AUTHENTICATION_RESPONSE = "process-outbound-auth-oidc-response";
+ public static final String INITIATE_OUTBOUND_AUTH_REQUEST = "initiate-outbound-auth-oidc-request";
+ }
+ }
}
diff --git a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java
index 24da6318..6eadf354 100644
--- a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java
+++ b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/main/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticator.java
@@ -52,6 +52,8 @@
import org.wso2.carbon.identity.application.common.model.Property;
import org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants;
import org.wso2.carbon.identity.base.IdentityConstants;
+import org.wso2.carbon.identity.central.log.mgt.utils.LogConstants;
+import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils;
import org.wso2.carbon.identity.claim.metadata.mgt.exception.ClaimMetadataException;
import org.wso2.carbon.identity.claim.metadata.mgt.model.ExternalClaim;
import org.wso2.carbon.identity.core.ServiceURLBuilder;
@@ -63,6 +65,7 @@
import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.UserStoreManager;
+import org.wso2.carbon.utils.DiagnosticLog;
import java.io.BufferedReader;
import java.io.IOException;
@@ -83,11 +86,15 @@
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static org.wso2.carbon.identity.application.authenticator.oidc.OIDCAuthenticatorConstants.Claim.NONCE;
+import static org.wso2.carbon.identity.application.authenticator.oidc.OIDCAuthenticatorConstants.LogConstants.ActionIDs.PROCESS_AUTHENTICATION_RESPONSE;
+import static org.wso2.carbon.identity.application.authenticator.oidc.OIDCAuthenticatorConstants.LogConstants.ActionIDs.INITIATE_OUTBOUND_AUTH_REQUEST;
+import static org.wso2.carbon.identity.application.authenticator.oidc.OIDCAuthenticatorConstants.LogConstants.OUTBOUND_AUTH_OIDC_SERVICE;
import static org.wso2.carbon.identity.application.authenticator.oidc.OIDCAuthenticatorConstants.OIDC_FEDERATION_NONCE;
import static org.wso2.carbon.identity.base.IdentityConstants.FEDERATED_IDP_SESSION_ID;
@@ -163,13 +170,17 @@ public boolean canHandle(HttpServletRequest request) {
if (LOG.isTraceEnabled()) {
LOG.trace("Inside OpenIDConnectAuthenticator.canHandle()");
}
-
- if (OIDCAuthenticatorConstants.LOGIN_TYPE.equals(getLoginType(request))) {
- return true;
+ boolean canHandle = OIDCAuthenticatorConstants.LOGIN_TYPE.equals(getLoginType(request));
+ if (canHandle && LoggerUtils.isDiagnosticLogsEnabled()) {
+ DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder = new DiagnosticLog.DiagnosticLogBuilder(
+ getComponentId(), FrameworkConstants.LogConstants.ActionIDs.HANDLE_AUTH_STEP);
+ diagnosticLogBuilder.resultStatus(DiagnosticLog.ResultStatus.SUCCESS)
+ .logDetailLevel(DiagnosticLog.LogDetailLevel.INTERNAL_SYSTEM)
+ .resultMessage("Outbound OIDC authenticator handling the authentication.");
+ LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder);
}
-
+ return canHandle;
// TODO : What if IdP failed?
- return false;
}
/**
@@ -382,8 +393,30 @@ protected void initiateAuthenticationRequest(HttpServletRequest request, HttpSer
AuthenticationContext context) throws AuthenticationFailedException {
try {
+ if (LoggerUtils.isDiagnosticLogsEnabled()) {
+ DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder = new DiagnosticLog.DiagnosticLogBuilder(
+ getComponentId(), INITIATE_OUTBOUND_AUTH_REQUEST);
+ diagnosticLogBuilder.resultMessage("Initiate outbound OIDC authentication request.")
+ .logDetailLevel(DiagnosticLog.LogDetailLevel.APPLICATION)
+ .resultStatus(DiagnosticLog.ResultStatus.SUCCESS)
+ .inputParam(LogConstants.InputKeys.STEP, context.getCurrentStep())
+ .inputParam(LogConstants.InputKeys.IDP, context.getExternalIdP().getIdPName())
+ .inputParams(getApplicationDetails(context));
+ LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder);
+ }
Map authenticatorProperties = context.getAuthenticatorProperties();
if (authenticatorProperties != null) {
+ DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder = null;
+ if (LoggerUtils.isDiagnosticLogsEnabled()) {
+ diagnosticLogBuilder = new DiagnosticLog.DiagnosticLogBuilder(getComponentId(),
+ INITIATE_OUTBOUND_AUTH_REQUEST);
+ diagnosticLogBuilder.logDetailLevel(DiagnosticLog.LogDetailLevel.APPLICATION)
+ .resultStatus(DiagnosticLog.ResultStatus.SUCCESS)
+ .inputParam(LogConstants.InputKeys.STEP, context.getCurrentStep())
+ .inputParam("authenticator properties", authenticatorProperties.keySet())
+ .inputParam(LogConstants.InputKeys.IDP, context.getExternalIdP().getIdPName())
+ .inputParams(getApplicationDetails(context));
+ }
String clientId = authenticatorProperties.get(OIDCAuthenticatorConstants.CLIENT_ID);
String authorizationEP = getOIDCAuthzEndpoint(authenticatorProperties);
String callbackurl = getCallbackUrl(authenticatorProperties);
@@ -397,6 +430,9 @@ protected void initiateAuthenticationRequest(HttpServletRequest request, HttpSer
String queryString = getQueryString(authenticatorProperties);
if (StringUtils.isNotBlank(scopes)) {
+ if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) {
+ diagnosticLogBuilder.inputParam("scopes", scopes);
+ }
queryString += "&scope=" + scopes;
}
queryString = interpretQueryString(context, queryString, request.getParameterMap());
@@ -458,6 +494,10 @@ protected void initiateAuthenticationRequest(HttpServletRequest request, HttpSer
}
}
response.sendRedirect(loginPage);
+ if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) {
+ diagnosticLogBuilder.resultMessage("Redirecting to the federated IDP login page.");
+ LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder);
+ }
} else {
if (LOG.isDebugEnabled()) {
LOG.debug(ErrorMessages.RETRIEVING_AUTHENTICATOR_PROPERTIES_FAILED.getMessage());
@@ -500,6 +540,17 @@ private String getOIDCAuthzEndpoint(Map authenticatorProperties)
protected void processAuthenticationResponse(HttpServletRequest request, HttpServletResponse response,
AuthenticationContext context) throws AuthenticationFailedException {
+ if (LoggerUtils.isDiagnosticLogsEnabled()) {
+ DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder = new DiagnosticLog.DiagnosticLogBuilder(
+ getComponentId(), PROCESS_AUTHENTICATION_RESPONSE);
+ diagnosticLogBuilder.resultMessage("Processing outbound OIDC authentication response.")
+ .logDetailLevel(DiagnosticLog.LogDetailLevel.APPLICATION)
+ .resultStatus(DiagnosticLog.ResultStatus.SUCCESS)
+ .inputParam(LogConstants.InputKeys.STEP, context.getCurrentStep())
+ .inputParam(LogConstants.InputKeys.IDP, context.getExternalIdP().getIdPName())
+ .inputParams(getApplicationDetails(context));
+ LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder);
+ }
// oAuthResponse can be null in some authentication flows. i.e Google One Tap.
OAuthClientResponse oAuthResponse = requestAccessToken(request, context);
// TODO : return access token and id token to framework
@@ -522,6 +573,15 @@ protected void processAuthenticationResponse(HttpServletRequest request, HttpSer
Map claimsMap = new HashMap<>();
Map jwtAttributeMap = new HashMap<>();
+ DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder = null;
+ if (LoggerUtils.isDiagnosticLogsEnabled()) {
+ diagnosticLogBuilder = new DiagnosticLog.DiagnosticLogBuilder(
+ getComponentId(), PROCESS_AUTHENTICATION_RESPONSE);
+ diagnosticLogBuilder.inputParam(LogConstants.InputKeys.STEP, context.getCurrentStep())
+ .inputParams(getApplicationDetails(context))
+ .inputParam(LogConstants.InputKeys.IDP, context.getExternalIdP().getIdPName())
+ .logDetailLevel(DiagnosticLog.LogDetailLevel.APPLICATION);
+ }
if (StringUtils.isNotBlank(idToken)) {
jwtAttributeMap = getIdTokenClaims(context, idToken);
if (jwtAttributeMap.isEmpty()) {
@@ -532,10 +592,15 @@ protected void processAuthenticationResponse(HttpServletRequest request, HttpSer
throw new AuthenticationFailedException(ErrorMessages.DECODED_JSON_OBJECT_IS_NULL.getCode(),
errorMessage);
}
-
+ if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) {
+ diagnosticLogBuilder.inputParam("id token claims", jwtAttributeMap.keySet());
+ }
String idpName = context.getExternalIdP().getIdPName();
String sidClaim = (String) jwtAttributeMap.get(OIDCAuthenticatorConstants.Claim.SID);
if (StringUtils.isNotBlank(sidClaim) && StringUtils.isNotBlank(idpName)) {
+ if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) {
+ diagnosticLogBuilder.inputParam("federated idp name", idpName);
+ }
// Add 'sid' claim into authentication context, to be stored in the UserSessionStore for single logout.
context.setProperty(FEDERATED_IDP_SESSION_ID + idpName, sidClaim);
}
@@ -574,6 +639,13 @@ protected void processAuthenticationResponse(HttpServletRequest request, HttpSer
claimsMap.putAll(getSubjectAttributes(oAuthResponse, authenticatorProperties));
authenticatedUser.setUserAttributes(claimsMap);
context.setSubject(authenticatedUser);
+ if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) {
+ diagnosticLogBuilder.resultMessage("Outbound OIDC authentication response processed successfully.")
+ .resultStatus(DiagnosticLog.ResultStatus.SUCCESS);
+ diagnosticLogBuilder.inputParam("user attributes (local claim : remote claim)",
+ getUserAttributeClaimMappingList(authenticatedUser));
+ LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder);
+ }
}
/**
@@ -1193,6 +1265,15 @@ protected String sendRequest(String url, String accessToken) throws IOException
return builder.toString();
}
+ /**
+ * Return the component ID of the Authenticator. This will be used for logging purposes.
+ * @return Component ID String.
+ */
+ protected String getComponentId() {
+
+ return OUTBOUND_AUTH_OIDC_SERVICE;
+ }
+
private String interpretQueryString(AuthenticationContext context, String queryString,
Map parameters) {
@@ -1309,4 +1390,31 @@ private AuthenticatorFlowStatus processLogout(HttpServletRequest request, HttpSe
return AuthenticatorFlowStatus.SUCCESS_COMPLETED;
}
}
+
+ /**
+ * Get application details from the authentication context.
+ * @param context Authentication context.
+ * @return Map of application details.
+ */
+ private Map getApplicationDetails(AuthenticationContext context) {
+
+ Map applicationDetailsMap = new HashMap<>();
+ FrameworkUtils.getApplicationResourceId(context).ifPresent(applicationId ->
+ applicationDetailsMap.put(LogConstants.InputKeys.APPLICATION_ID, applicationId));
+ FrameworkUtils.getApplicationName(context).ifPresent(applicationName ->
+ applicationDetailsMap.put(LogConstants.InputKeys.APPLICATION_NAME,
+ applicationName));
+ return applicationDetailsMap;
+ }
+
+ private static List getUserAttributeClaimMappingList(AuthenticatedUser authenticatedUser) {
+
+ return authenticatedUser.getUserAttributes().keySet().stream()
+ .map(claimMapping -> {
+ String localClaim = claimMapping.getLocalClaim().getClaimUri();
+ String remoteClaim = claimMapping.getRemoteClaim().getClaimUri();
+ return localClaim + " : " + remoteClaim;
+ })
+ .collect(Collectors.toList());
+ }
}
diff --git a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/test/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticatorTest.java b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/test/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticatorTest.java
index 8af8a6df..72863c82 100644
--- a/components/org.wso2.carbon.identity.application.authenticator.oidc/src/test/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticatorTest.java
+++ b/components/org.wso2.carbon.identity.application.authenticator.oidc/src/test/java/org/wso2/carbon/identity/application/authenticator/oidc/OpenIDConnectAuthenticatorTest.java
@@ -47,7 +47,9 @@
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils;
import org.wso2.carbon.identity.application.authenticator.oidc.internal.OpenIDConnectAuthenticatorDataHolder;
import org.wso2.carbon.identity.application.common.model.ClaimMapping;
+import org.wso2.carbon.identity.application.common.model.IdentityProvider;
import org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants;
+import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils;
import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataManagementService;
import org.wso2.carbon.identity.core.ServiceURL;
import org.wso2.carbon.identity.core.ServiceURLBuilder;
@@ -89,7 +91,7 @@
*/
@PrepareForTest({LogFactory.class, OAuthClient.class, URL.class, FrameworkUtils.class,
OpenIDConnectAuthenticatorDataHolder.class, OAuthAuthzResponse.class, OAuthClientRequest.class,
- OAuthClientResponse.class, IdentityUtil.class, OpenIDConnectAuthenticator.class, ServiceURLBuilder.class})
+ OAuthClientResponse.class, IdentityUtil.class, OpenIDConnectAuthenticator.class, ServiceURLBuilder.class, LoggerUtils.class})
public class OpenIDConnectAuthenticatorTest extends PowerMockTestCase {
@Mock
@@ -218,6 +220,8 @@ public Object[][] getRequestStatus() {
@Test(dataProvider = "requestDataHandler")
public void testCanHandle(String grantType, String state, String loginType, String error, String expectedCanHandler, String expectedContext, String msgCanHandler, String msgContext) throws IOException {
+ mockStatic(LoggerUtils.class);
+ when(LoggerUtils.isDiagnosticLogsEnabled()).thenReturn(true);
when(mockServletRequest.getParameter(OIDCAuthenticatorConstants.OAUTH2_GRANT_TYPE_CODE)).thenReturn(grantType);
when(mockServletRequest.getParameter(OIDCAuthenticatorConstants.OAUTH2_PARAM_STATE)).thenReturn(state);
when(mockServletRequest.getParameter(OIDCAuthenticatorConstants.LOGIN_TYPE)).thenReturn(loginType);
@@ -386,6 +390,8 @@ public void testGetQueryStringWithMultipleAuthenticatorParam() throws Exception
public void testInitiateAuthenticationRequestNullProperties() throws OAuthSystemException,
OAuthProblemException, AuthenticationFailedException, UserStoreException {
+ mockStatic(LoggerUtils.class);
+ when(LoggerUtils.isDiagnosticLogsEnabled()).thenReturn(true);
mockAuthenticationRequestContext(mockAuthenticationContext);
when(mockAuthenticationContext.getAuthenticatorProperties()).thenReturn(null);
openIDConnectAuthenticator.initiateAuthenticationRequest(mockServletRequest, mockServletResponse,
@@ -454,6 +460,8 @@ public void testPassProcessAuthenticationWithBlankCallBack() throws Exception {
authenticatorProperties.put("callbackUrl", " ");
mockStatic(IdentityUtil.class);
when(IdentityUtil.getServerURL(FrameworkConstants.COMMONAUTH, true, true)).thenReturn("http:/localhost:9443/oauth2/callback");
+ mockStatic(LoggerUtils.class);
+ when(LoggerUtils.isDiagnosticLogsEnabled()).thenReturn(true);
setParametersForOAuthClientResponse(mockOAuthClientResponse, accessToken, idToken);
when(openIDConnectAuthenticatorDataHolder.getClaimMetadataManagementService()).thenReturn
(claimMetadataManagementService);
@@ -484,6 +492,7 @@ public void testFailProcessAuthenticationWhenNonceMisMatch() throws Exception {
public void testPassProcessAuthenticationWithParamValue() throws Exception {
setupTest();
+ when(LoggerUtils.isDiagnosticLogsEnabled()).thenReturn(true);
authenticatorProperties.put("callbackUrl", "http://localhost:8080/playground2/oauth2client");
Map paramMap = new HashMap<>();
paramMap.put("redirect_uri", "http:/localhost:9443/oauth2/redirect");
@@ -657,6 +666,7 @@ private void setupTest() throws Exception {
when(mockOAuthzResponse.getCode()).thenReturn("200");
when(mockAuthenticationContext.getProperty(OIDCAuthenticatorConstants.ACCESS_TOKEN)).thenReturn(accessToken);
when(mockAuthenticationContext.getProperty(OIDCAuthenticatorConstants.ID_TOKEN)).thenReturn(idToken);
+ when(mockAuthenticationContext.getExternalIdP()).thenReturn(getDummyExternalIdPConfig());
setParametersForOAuthClientResponse(mockOAuthClientResponse, accessToken, idToken);
mockStatic(OpenIDConnectAuthenticatorDataHolder.class);
when(OpenIDConnectAuthenticatorDataHolder.getInstance()).thenReturn(openIDConnectAuthenticatorDataHolder);
@@ -677,6 +687,9 @@ private void setupTest() throws Exception {
when(serviceURLBuilder.addPath(anyString())).thenReturn(serviceURLBuilder);
when(serviceURLBuilder.addParameter(anyString(), anyString())).thenReturn(serviceURLBuilder);
when(serviceURLBuilder.build()).thenReturn(serviceURL);
+
+ mockStatic(LoggerUtils.class);
+ when(LoggerUtils.isDiagnosticLogsEnabled()).thenReturn(true);
}
private void setParametersForOAuthClientResponse(OAuthClientResponse mockOAuthClientResponse,
@@ -692,5 +705,13 @@ private void mockAuthenticationRequestContext(AuthenticationContext mockAuthenti
paramValueMap = new HashMap<>();
when(mockAuthenticationContext.getProperty("oidc:param.map")).thenReturn(paramValueMap);
when(mockAuthenticationContext.getContextIdentifier()).thenReturn("");
+ when(mockAuthenticationContext.getExternalIdP()).thenReturn(getDummyExternalIdPConfig());
+ }
+
+ private ExternalIdPConfig getDummyExternalIdPConfig() {
+
+ IdentityProvider identityProvider = new IdentityProvider();
+ identityProvider.setIdentityProviderName("DummyIDPName");
+ return new ExternalIdPConfig(identityProvider);
}
}
diff --git a/pom.xml b/pom.xml
index 693180e1..6f3a4184 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,6 +74,11 @@
org.wso2.carbon.identity.application.common
${carbon.identity.framework.version}
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.central.log.mgt
+ ${carbon.identity.framework.version}
+
org.wso2.carbon.identity.framework
org.wso2.carbon.identity.testutil
@@ -195,6 +200,12 @@
+
+ org.wso2.carbon.identity.organization.management.core
+ org.wso2.carbon.identity.organization.management.service
+ ${identity.organization.management.core.version}
+ test
+
com.h2database
h2
@@ -293,11 +304,11 @@
${project.version}
- 5.25.90
+ 5.25.260
1.0.0.wso2v3
2.4.7
3.0.0.wso2v2
- 4.9.0
+ 4.9.10
1.4
1.5.3
1.6.1
@@ -309,15 +320,17 @@
6.2.0
6.4.158
+ 1.0.50
+
2.6.0.wso2v1
3.2.2.wso2v1
1.14.0.wso2v1
[6.0.0, 7.0.0)
- [5.14.0, 7.0.0)
+ [5.25.260, 7.0.0)
- [4.5.0, 5.0.0)
+ [4.9.10, 5.0.0)
[6.2.0, 7.0.0)
[4.4.0, 5.0.0)