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)