diff --git a/open-banking-accelerator/accelerators/ob-is/carbon-home/repository/resources/conf/templates/repository/conf/open-banking.xml.j2 b/open-banking-accelerator/accelerators/ob-is/carbon-home/repository/resources/conf/templates/repository/conf/open-banking.xml.j2 index a635b51e..eae3cf02 100644 --- a/open-banking-accelerator/accelerators/ob-is/carbon-home/repository/resources/conf/templates/repository/conf/open-banking.xml.j2 +++ b/open-banking-accelerator/accelerators/ob-is/carbon-home/repository/resources/conf/templates/repository/conf/open-banking.xml.j2 @@ -152,6 +152,47 @@ {% endif %} + + + {% if open_banking.identity.ciba.auth_web_link.redirect_endpoint is defined %} + {{open_banking.identity.ciba.auth_web_link.redirect_endpoint}} + {% else %} + ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/ciba.jsp + {% endif %} + + {% if open_banking.identity.ciba.auth_web_link.allowed_auth_url_parameters is defined %} + {% for value in open_banking.identity.ciba.auth_web_link.allowed_auth_url_parameters %} + {{value}} + {% endfor %} + {% else %} + client_id + scope + response_type + nonce + redirect_uri + binding_message + {% endif %} + + {% if open_banking.identity.ciba.auth_web_link.notification_provider is defined %} + {{open_banking.identity.ciba.auth_web_link.notification_provider}} + {% else %} + com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink.notification.provider.SMSNotificationProvider + {% endif %} + + + {% if open_banking.identity.ciba.auth_web_link.sms_notification.sms_url is defined %} + {{open_banking.identity.ciba.auth_web_link.sms_notification.sms_url}} + {% else %} + ${carbon.protocol}://${carbon.host}:${carbon.management.port}/sample/sms + {% endif %} + + {% for header in open_banking.identity.ciba.auth_web_link.sms_notification.header %} +
+ {% endfor %} + + + + {% if open_banking.identity.authentication_webapp.servlet_extension is defined %} diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.ciba/src/main/java/com.wso2.openbanking.accelerator.ciba/OBCIBARequestObjectValidationExtension.java b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.ciba/src/main/java/com.wso2.openbanking.accelerator.ciba/OBCIBARequestObjectValidationExtension.java index 7b0251dc..c66a2c59 100644 --- a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.ciba/src/main/java/com.wso2.openbanking.accelerator.ciba/OBCIBARequestObjectValidationExtension.java +++ b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.ciba/src/main/java/com.wso2.openbanking.accelerator.ciba/OBCIBARequestObjectValidationExtension.java @@ -18,9 +18,15 @@ package com.wso2.openbanking.accelerator.ciba; +import com.wso2.openbanking.accelerator.common.constant.OpenBankingConstants; +import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; import com.wso2.openbanking.accelerator.common.util.Generated; +import com.wso2.openbanking.accelerator.consent.mgt.dao.models.DetailedConsentResource; +import com.wso2.openbanking.accelerator.identity.internal.IdentityExtensionsDataHolder; import net.minidev.json.JSONObject; import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.wso2.carbon.identity.oauth2.RequestObjectException; import org.wso2.carbon.identity.oauth2.model.OAuth2Parameters; import org.wso2.carbon.identity.openidconnect.CIBARequestObjectValidatorImpl; @@ -34,6 +40,8 @@ */ public class OBCIBARequestObjectValidationExtension extends CIBARequestObjectValidatorImpl { + private static final Log log = LogFactory.getLog(OBCIBARequestObjectValidationExtension.class); + /** * Validations related to clientId, response type, exp, redirect URL, mandatory params, * issuer, audience are done. Called after signature validation. @@ -56,10 +64,32 @@ public boolean validateRequestObject(RequestObject initialRequestObject, OAuth2P if (StringUtils.isEmpty(intent.getAsString(CIBAConstants.VALUE_TAG))) { throw new RequestObjectException(CIBAConstants.INVALID_REQUEST, CIBAConstants.EMPTY_CONTENT_ERROR); } - + if (!isAuthorizableConsent(intent.getAsString("value"))) { + throw new RequestObjectException(OAuth2ErrorCodes.INVALID_REQUEST, + "Consent is not in authorizable state"); + } return validateIAMConstraints(initialRequestObject, oAuth2Parameters); } + private boolean isAuthorizableConsent(String consentId) throws RequestObjectException { + try { + DetailedConsentResource detailedConsent = IdentityExtensionsDataHolder.getInstance() + .getConsentCoreService().getDetailedConsent(consentId); + if (log.isDebugEnabled()) { + log.debug(String.format("Consent status for consent_id %s is %s", + detailedConsent.getConsentID(), detailedConsent.getCurrentStatus())); + } + return OpenBankingConstants.AWAITING_AUTHORISATION_STATUS.equalsIgnoreCase( + detailedConsent.getCurrentStatus()) || + OpenBankingConstants.AWAITING_FURTHER_AUTHORISATION_STATUS + .equalsIgnoreCase(detailedConsent.getCurrentStatus()); + } catch (ConsentManagementException e) { + log.error("Error occurred while fetching consent_id", e); + throw new RequestObjectException(OAuth2ErrorCodes.INVALID_REQUEST, + "Error occurred while fetching consent_id", e); + } + } + /** * Validate IAM related logic. * @param requestObject diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.ciba/src/test/java/com/wso2/openbanking/accelerator/ciba/OBCIBARequestObjectValidationExtensionTest.java b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.ciba/src/test/java/com/wso2/openbanking/accelerator/ciba/OBCIBARequestObjectValidationExtensionTest.java index 74133c1a..e3cabab2 100644 --- a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.ciba/src/test/java/com/wso2/openbanking/accelerator/ciba/OBCIBARequestObjectValidationExtensionTest.java +++ b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.ciba/src/test/java/com/wso2/openbanking/accelerator/ciba/OBCIBARequestObjectValidationExtensionTest.java @@ -19,26 +19,53 @@ package com.wso2.openbanking.accelerator.ciba; import com.nimbusds.jwt.JWTClaimsSet; +import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; +import com.wso2.openbanking.accelerator.consent.mgt.dao.models.DetailedConsentResource; +import com.wso2.openbanking.accelerator.consent.mgt.service.impl.ConsentCoreServiceImpl; +import com.wso2.openbanking.accelerator.identity.internal.IdentityExtensionsDataHolder; import net.minidev.json.JSONObject; import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.testng.PowerMockTestCase; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.wso2.carbon.identity.oauth2.RequestObjectException; import org.wso2.carbon.identity.oauth2.model.OAuth2Parameters; import org.wso2.carbon.identity.openidconnect.model.RequestObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** * Test class for OBCIBARequestObjectValidationExtension. */ @PowerMockIgnore("jdk.internal.reflect.*") -@PrepareForTest({JWTClaimsSet.class, OAuth2Parameters.class, RequestObject.class, JSONObject.class}) +@PrepareForTest({JWTClaimsSet.class, OAuth2Parameters.class, RequestObject.class, JSONObject.class, + IdentityExtensionsDataHolder.class}) public class OBCIBARequestObjectValidationExtensionTest extends PowerMockTestCase { private final String dummyString = "dummyString"; + private static ConsentCoreServiceImpl consentCoreServiceMock; + + @BeforeClass + public void initTest() { + + consentCoreServiceMock = PowerMockito.mock(ConsentCoreServiceImpl.class); + } + + @BeforeMethod + private void mockStaticClasses() throws ConsentManagementException { + + PowerMockito.mockStatic(IdentityExtensionsDataHolder.class); + IdentityExtensionsDataHolder mock = PowerMockito.mock(IdentityExtensionsDataHolder.class); + PowerMockito.when(IdentityExtensionsDataHolder.getInstance()).thenReturn(mock); + PowerMockito.when(IdentityExtensionsDataHolder.getInstance().getConsentCoreService()) + .thenReturn(consentCoreServiceMock); + } @Test(expectedExceptions = RequestObjectException.class, description = "Empty intent key") public void validateRequestObjectInvalidIntentKeyTest() throws Exception { @@ -61,6 +88,30 @@ public void validateRequestObjectInvalidIntentKeyTest() throws Exception { } + @Test(expectedExceptions = RequestObjectException.class, description = "Consent is not in authorizable state") + public void validateRequestObjectInvalidConsentIdTest() throws Exception { + + OBCIBARequestObjectValidationExtensionMock obcibaRequestObjectValidationExtensionMock = + new OBCIBARequestObjectValidationExtensionMock(); + + JSONObject intent = mock(JSONObject.class); + + RequestObject requestObject = mock(RequestObject.class); + OAuth2Parameters oAuth2Parameters = mock(OAuth2Parameters.class); + JWTClaimsSet claimsSet = Mockito.mock(JWTClaimsSet.class); + + Mockito.when(requestObject.getClaimsSet()).thenReturn(claimsSet); + Mockito.when(claimsSet.getJSONObjectClaim(Mockito.anyString())).thenReturn(intent); + when(intent.getAsString("value")).thenReturn(dummyString); + + DetailedConsentResource consentResourceMock = mock(DetailedConsentResource.class); + doReturn("authorised").when(consentResourceMock).getCurrentStatus(); + doReturn(consentResourceMock).when(consentCoreServiceMock).getDetailedConsent(anyString()); + + obcibaRequestObjectValidationExtensionMock.validateRequestObject(requestObject, oAuth2Parameters); + + } + @Test(description = "success scenario") public void validateRequestObjectValidObjectTest() throws Exception { @@ -78,6 +129,10 @@ public void validateRequestObjectValidObjectTest() throws Exception { when(intent.getAsString("value")).thenReturn(dummyString); + DetailedConsentResource consentResourceMock = mock(DetailedConsentResource.class); + doReturn("awaitingAuthorisation").when(consentResourceMock).getCurrentStatus(); + doReturn(consentResourceMock).when(consentCoreServiceMock).getDetailedConsent(anyString()); + obcibaRequestObjectValidationExtensionMock.validateRequestObject(requestObject, oAuth2Parameters); } diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/config/OpenBankingConfigParser.java b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/config/OpenBankingConfigParser.java index 9debdae1..511e3d90 100644 --- a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/config/OpenBankingConfigParser.java +++ b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/config/OpenBankingConfigParser.java @@ -1489,4 +1489,97 @@ public boolean isNbfClaimMandatory() { getConfigElementFromKey(OpenBankingConstants.MANDATE_NBF_CLAIM)).trim()); } + /** + * Method to get the CIBA authentication redirect endpoint configuration. + * + * @return ciba redirect endpoint + */ + public String getCibaAuthenticationRedirectEndpoint() { + return getConfigElementFromKey(OpenBankingConstants.CIBA_AUTHENTICATION_REDIRECT_ENDPOINT) == null ? + "" : ((String) getConfigElementFromKey( + OpenBankingConstants.CIBA_AUTHENTICATION_REDIRECT_ENDPOINT)).trim(); + } + + /** + * Method to get the CIBA web link allowed parameters. + * + * @return list of allowed parameters + */ + public List getCibaWebLinkAllowedParams() { + + List allowedParamsList = new ArrayList<>(); + Object configElementFromKey = getConfigElementFromKey(OpenBankingConstants.CIBA_WEB_LINK_ALLOWED_PARAMETERS); + if (configElementFromKey instanceof List) { + allowedParamsList = (List) configElementFromKey; + } else { + allowedParamsList.add(configElementFromKey.toString()); + } + return allowedParamsList; + } + + /** + * Method to get the CIBA notification Provider + * + * @return CIBA notification Provider + */ + public String getCibaWebLinkNotificationProvider() { + + return getConfigElementFromKey(OpenBankingConstants.CIBA_NOTIFICATION_PROVIDER) == null ? + "" : ((String) getConfigElementFromKey(OpenBankingConstants.CIBA_NOTIFICATION_PROVIDER)).trim(); + } + + /** + * Method to get the CIBA SMS notification service URL + * + * @return sms service URL + */ + public String getCibaWebLinkSMSNotificationServiceURL() { + + return getConfigElementFromKey(OpenBankingConstants.CIBA_WEB_LINK_NOTIFICATION_SMS_SERVICE_URL) == null ? + "" : ((String) getConfigElementFromKey( + OpenBankingConstants.CIBA_WEB_LINK_NOTIFICATION_SMS_SERVICE_URL)).trim(); + } + + /** + * Method to get the CIBA web link SMS notification request headers + * + * @return A map of header name and values + */ + public Map getCibaWebLinkSMSNotificationRequestHeaders() { + + Map headersMap = new HashMap<>(); + OMElement identityElement = rootElement.getFirstChildWithName( + new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.IDENTITY_CONFIG_TAG)); + + if (identityElement != null) { + OMElement cibaElement = identityElement.getFirstChildWithName( + new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.CIBA_CONFIG_TAG)); + + if (cibaElement != null) { + OMElement authWebLinkElement = cibaElement.getFirstChildWithName( + new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.AUTH_WEB_LINK_CONFIG_TAG)); + + if (authWebLinkElement != null) { + OMElement smsElement = authWebLinkElement.getFirstChildWithName( + new QName(OpenBankingConstants.OB_CONFIG_QNAME, OpenBankingConstants.SMS_CONFIG_TAG)); + + if (smsElement != null) { + OMElement headersElement = smsElement.getFirstChildWithName( + new QName(OpenBankingConstants.OB_CONFIG_QNAME, + OpenBankingConstants.HEADERS_CONFIG_TAG)); + if (headersElement != null) { + Iterator headerElements = headersElement.getChildElements(); + while (headerElements.hasNext()) { + OMElement headerElement = (OMElement) headerElements.next(); + String headerName = headerElement.getAttributeValue(new QName("name")); + String headerValue = headerElement.getAttributeValue(new QName("value")); + headersMap.put(headerName, headerValue); + } + } + } + } + } + } + return headersMap; + } } diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/constant/OpenBankingConstants.java b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/constant/OpenBankingConstants.java index 6cf4d04a..952589a2 100644 --- a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/constant/OpenBankingConstants.java +++ b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/constant/OpenBankingConstants.java @@ -270,5 +270,33 @@ public class OpenBankingConstants { public static final String DOT_SEPARATOR = "."; public static final String MANDATE_NBF_CLAIM = "Identity.RequestObject.MandateNBF"; + // CIBA Constants + public static final String CIBA_AUTHENTICATION_REDIRECT_ENDPOINT = + "Identity.CIBA.AuthWebLink.AuthenticationRedirectEndpoint"; + public static final String CIBA_WEB_LINK_ALLOWED_PARAMETERS = + "Identity.CIBA.AuthWebLink.AllowedAuthURLParams.Value"; + public static final String CIBA_NOTIFICATION_PROVIDER = "Identity.CIBA.AuthWebLink.NotificationProvider"; + public static final String AUTH_REQ_ID = "auth_req_id"; + public static final String CIBA_WEB_AUTH_LINK_PARAM = "ciba_web_auth_link"; + public static final String CIBA_AUTH_CODE_RESPONSE_TYPE = "cibaAuthCode"; + + // CIBA SMS Constants + public static final String CIBA_WEB_LINK_NOTIFICATION_SMS_SERVICE_URL = + "Identity.CIBA.AuthWebLink.SMS.SMSUrl"; + public static final String IDENTITY_CONFIG_TAG = "Identity"; + public static final String CIBA_CONFIG_TAG = "CIBA"; + public static final String AUTH_WEB_LINK_CONFIG_TAG = "AuthWebLink"; + public static final String SMS_CONFIG_TAG = "SMS"; + public static final String HEADERS_CONFIG_TAG = "Headers"; + + // Accelerator default consent statuses + public static final String AUTHORISED_STATUS = "authorised"; + public static final String REJECTED_STATUS = "rejected"; + public static final String AWAITING_AUTHORISATION_STATUS = "awaitingAuthorisation"; + public static final String AWAITING_FURTHER_AUTHORISATION_STATUS = "awaitingFurtherAuthorisation"; + public static final String CREATED_AUTHORISATION_RESOURCE_STATE = "created"; + public static final String MULTI_AUTH_AUTHORISATION_TYPE = "multi-authorization"; + public static final String CARBON_SUPER_TENANT_DOMAIN = "@carbon.super"; + } diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/util/CarbonUtils.java b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/util/CarbonUtils.java index d25f6461..a8bedac3 100644 --- a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/util/CarbonUtils.java +++ b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/main/java/com/wso2/openbanking/accelerator/common/util/CarbonUtils.java @@ -71,7 +71,7 @@ public static String getCarbonHome() { @Generated(message = "Ignoring because ServerConfiguration cannot be mocked") public static String getCarbonPort() { - int offset = Integer.parseInt(ServerConfiguration.getInstance().getFirstProperty("Offset")); + int offset = Integer.parseInt(ServerConfiguration.getInstance().getFirstProperty("Ports.Offset")); return String.valueOf(9443 + offset); } diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/test/java/com/wso2/openbanking/accelerator/common/test/OBConfigParserTests.java b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/test/java/com/wso2/openbanking/accelerator/common/test/OBConfigParserTests.java index cba3464d..ddb56045 100644 --- a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/test/java/com/wso2/openbanking/accelerator/common/test/OBConfigParserTests.java +++ b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/test/java/com/wso2/openbanking/accelerator/common/test/OBConfigParserTests.java @@ -476,4 +476,32 @@ public void testNbfClaimMandatory() { boolean nbfClaimMandatory = openBankingConfigParser.isNbfClaimMandatory(); Assert.assertTrue(nbfClaimMandatory); } + + @Test(priority = 37) + public void testCibaWebLinkConfigs() { + String dummyConfigFile = absolutePathForTestResources + "/open-banking.xml"; + OpenBankingConfigParser openBankingConfigParser = OpenBankingConfigParser.getInstance(dummyConfigFile); + + List cibaWebLinkAllowedParams = openBankingConfigParser.getCibaWebLinkAllowedParams(); + Assert.assertEquals(cibaWebLinkAllowedParams.size(), 6); + Assert.assertEquals(cibaWebLinkAllowedParams.get(0), "client_id"); + Assert.assertEquals(openBankingConfigParser.getCibaWebLinkNotificationProvider(), + "com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink." + + "notification.provider.SMSNotificationProvider"); + Assert.assertEquals(openBankingConfigParser.getCibaAuthenticationRedirectEndpoint(), + "https://localhost:9446/authenticationendpoint/ciba.jsp"); + } + + @Test(priority = 38) + public void testCibaWebLinkSMSConfigs() { + String dummyConfigFile = absolutePathForTestResources + "/open-banking.xml"; + OpenBankingConfigParser openBankingConfigParser = OpenBankingConfigParser.getInstance(dummyConfigFile); + + String cibaWebLinkAllowedParams = openBankingConfigParser.getCibaWebLinkSMSNotificationServiceURL(); + Assert.assertEquals(cibaWebLinkAllowedParams, "https://localhost:9446/sample/sms"); + Map headerMap = openBankingConfigParser.getCibaWebLinkSMSNotificationRequestHeaders(); + Assert.assertEquals(headerMap.get("Authorization"), "abc"); + Assert.assertEquals(headerMap.get("Accept"), "application/json"); + } + } diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/test/resources/open-banking.xml b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/test/resources/open-banking.xml index 3d50008b..ec7fba9c 100644 --- a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/test/resources/open-banking.xml +++ b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.common/src/test/resources/open-banking.xml @@ -45,6 +45,27 @@ sampleServletExtension + + + https://localhost:9446/authenticationendpoint/ciba.jsp + + client_id + scope + response_type + nonce + redirect_uri + binding_message + + com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink.notification.provider.SMSNotificationProvider + + https://localhost:9446/sample/sms + +
+
+ + + + sampleCIBAServletExtension diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.identity/src/main/java/com/wso2/openbanking/accelerator/identity/auth/extensions/response/handler/OBCibaResponseTypeHandler.java b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.identity/src/main/java/com/wso2/openbanking/accelerator/identity/auth/extensions/response/handler/OBCibaResponseTypeHandler.java new file mode 100644 index 00000000..2b72cd22 --- /dev/null +++ b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.identity/src/main/java/com/wso2/openbanking/accelerator/identity/auth/extensions/response/handler/OBCibaResponseTypeHandler.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 com.wso2.openbanking.accelerator.identity.auth.extensions.response.handler; + +import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; +import com.wso2.openbanking.accelerator.common.constant.OpenBankingConstants; +import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; +import com.wso2.openbanking.accelerator.identity.internal.IdentityExtensionsDataHolder; +import org.apache.commons.lang3.StringUtils; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; +import org.wso2.carbon.identity.oauth.ciba.dao.CibaDAOFactory; +import org.wso2.carbon.identity.oauth.ciba.exceptions.CibaCoreException; +import org.wso2.carbon.identity.oauth.ciba.handlers.CibaResponseTypeHandler; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.authz.OAuthAuthzReqMessageContext; +import org.wso2.carbon.identity.oauth2.dto.OAuth2AuthorizeReqDTO; +import org.wso2.carbon.identity.oauth2.dto.OAuth2AuthorizeRespDTO; + +import java.util.ArrayList; + +/** + * Handles authorize requests with CibaAuthCode as response type. + */ +public class OBCibaResponseTypeHandler extends CibaResponseTypeHandler { + + @Override + public OAuth2AuthorizeRespDTO issue(OAuthAuthzReqMessageContext oauthAuthzMsgCtx) throws IdentityOAuth2Exception { + + OAuth2AuthorizeReqDTO authorizationReqDTO = oauthAuthzMsgCtx.getAuthorizationReqDTO(); + try { + // Assigning authenticated user for the request that to be persisted. + AuthenticatedUser cibaAuthenticatedUser = authorizationReqDTO.getUser(); + // Assigning the authentication status that to be persisted. + + ArrayList consentIds = IdentityExtensionsDataHolder.getInstance().getConsentCoreService() + .getConsentIdByConsentAttributeNameAndValue(OpenBankingConstants.AUTH_REQ_ID, + authorizationReqDTO.getNonce()); + + if (!consentIds.isEmpty()) { + if (IdentityExtensionsDataHolder.getInstance().getConsentCoreService().getDetailedConsent( + consentIds.get(0)).getCurrentStatus().equals(OpenBankingConstants.AUTHORISED_STATUS)) { + // Update successful authentication. + String authCodeKey = CibaDAOFactory.getInstance().getCibaAuthMgtDAO() + .getCibaAuthCodeKey(authorizationReqDTO.getNonce()); + CibaDAOFactory.getInstance().getCibaAuthMgtDAO() + .persistAuthenticationSuccess(authCodeKey, cibaAuthenticatedUser); + } + } + String callbackURL = OpenBankingConfigParser.getInstance().getCibaAuthenticationRedirectEndpoint(); + if (StringUtils.isNotEmpty(callbackURL)) { + OAuth2AuthorizeRespDTO respDTO = new OAuth2AuthorizeRespDTO(); + respDTO.setCallbackURI(callbackURL); + return respDTO; + } else { + throw new IdentityOAuth2Exception("Error occurred while retrieving CIBA redirect endpoint."); + } + } catch (CibaCoreException e) { + throw new IdentityOAuth2Exception("Error occurred in persisting authenticated user and authentication " + + "status for the request made by client: " + authorizationReqDTO.getConsumerKey(), e); + } catch (ConsentManagementException e) { + throw new IdentityOAuth2Exception("Error occurred in retrieving auth_req_id ", e); + } + } +} diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.identity/src/main/java/com/wso2/openbanking/accelerator/identity/auth/extensions/response/validator/OBCibaResponseTypeValidator.java b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.identity/src/main/java/com/wso2/openbanking/accelerator/identity/auth/extensions/response/validator/OBCibaResponseTypeValidator.java new file mode 100644 index 00000000..d41a98f5 --- /dev/null +++ b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.identity/src/main/java/com/wso2/openbanking/accelerator/identity/auth/extensions/response/validator/OBCibaResponseTypeValidator.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 com.wso2.openbanking.accelerator.identity.auth.extensions.response.validator; + +import com.wso2.openbanking.accelerator.common.util.Generated; +import org.apache.oltu.oauth2.common.exception.OAuthProblemException; +import org.wso2.carbon.identity.oauth.ciba.handlers.CibaResponseTypeValidator; + +import javax.servlet.http.HttpServletRequest; + +/** + * Validates authorize responses with cibaAuthCode as response type. + */ +@Generated(message = "Ignoring since method do not contain a logic") +public class OBCibaResponseTypeValidator extends CibaResponseTypeValidator { + + @Override + @Generated(message = "Ignoring since method do not contain a logic") + public void validateContentType(HttpServletRequest request) throws OAuthProblemException { + // Overriding content type validation + // This is for browser flow with cibaAuthCode response type. (Web-Auth link scenario) + } + +} diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.identity/src/test/java/com/wso2/openbanking/accelerator/identity/auth/extensions/response/handler/OBCibaResponseTypeHandlerTest.java b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.identity/src/test/java/com/wso2/openbanking/accelerator/identity/auth/extensions/response/handler/OBCibaResponseTypeHandlerTest.java new file mode 100644 index 00000000..50815ea6 --- /dev/null +++ b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.identity/src/test/java/com/wso2/openbanking/accelerator/identity/auth/extensions/response/handler/OBCibaResponseTypeHandlerTest.java @@ -0,0 +1,196 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 com.wso2.openbanking.accelerator.identity.auth.extensions.response.handler; + +import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; +import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; +import com.wso2.openbanking.accelerator.consent.mgt.dao.models.DetailedConsentResource; +import com.wso2.openbanking.accelerator.consent.mgt.service.impl.ConsentCoreServiceImpl; +import com.wso2.openbanking.accelerator.identity.internal.IdentityExtensionsDataHolder; +import org.mockito.Mock; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.testng.PowerMockTestCase; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; +import org.wso2.carbon.identity.oauth.ciba.dao.CibaDAOFactory; +import org.wso2.carbon.identity.oauth.ciba.dao.CibaMgtDAO; +import org.wso2.carbon.identity.oauth.ciba.exceptions.CibaCoreException; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.authz.OAuthAuthzReqMessageContext; +import org.wso2.carbon.identity.oauth2.dto.OAuth2AuthorizeReqDTO; + +import java.util.ArrayList; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; + +@PowerMockIgnore("jdk.internal.reflect.*") +@PrepareForTest({OpenBankingConfigParser.class, IdentityExtensionsDataHolder.class, CibaDAOFactory.class}) +public class OBCibaResponseTypeHandlerTest extends PowerMockTestCase { + + @Mock + ConsentCoreServiceImpl consentCoreServiceMock; + @Mock + OpenBankingConfigParser openBankingConfigParser; + + @Mock + CibaDAOFactory cibaDAOFactory; + + @BeforeMethod + private void mockStaticClasses() throws ConsentManagementException { + + PowerMockito.mockStatic(IdentityExtensionsDataHolder.class); + IdentityExtensionsDataHolder mock = PowerMockito.mock(IdentityExtensionsDataHolder.class); + PowerMockito.when(IdentityExtensionsDataHolder.getInstance()).thenReturn(mock); + PowerMockito.when(IdentityExtensionsDataHolder.getInstance().getConsentCoreService()) + .thenReturn(consentCoreServiceMock); + + PowerMockito.mockStatic(OpenBankingConfigParser.class); + openBankingConfigParser = PowerMockito.mock(OpenBankingConfigParser.class); + PowerMockito.when(OpenBankingConfigParser.getInstance()) + .thenReturn(openBankingConfigParser); + + PowerMockito.mockStatic(CibaDAOFactory.class); + cibaDAOFactory = PowerMockito.mock(CibaDAOFactory.class); + PowerMockito.when(CibaDAOFactory.getInstance()) + .thenReturn(cibaDAOFactory); + } + + @Test() + public void obCibaResponseTypeHandlerSuccessTest() throws Exception { + + PowerMockito.when(OpenBankingConfigParser.getInstance().getCibaAuthenticationRedirectEndpoint()) + .thenReturn("testURL"); + OBCibaResponseTypeHandler obCibaResponseTypeHandler = new OBCibaResponseTypeHandler(); + OAuthAuthzReqMessageContext oAuthAuthzReqMessageContext = mock(OAuthAuthzReqMessageContext.class); + AuthenticatedUser cibaAuthenticatedUser = mock(AuthenticatedUser.class); + OAuth2AuthorizeReqDTO authorizationReqDTO = mock(OAuth2AuthorizeReqDTO.class); + doReturn(authorizationReqDTO).when(oAuthAuthzReqMessageContext).getAuthorizationReqDTO(); + doReturn(cibaAuthenticatedUser).when(authorizationReqDTO).getUser(); + obCibaResponseTypeHandler.issue(oAuthAuthzReqMessageContext); + + } + + @Test() + public void obCibaResponseTypeHandlerValidConsentIdTest() throws Exception { + + PowerMockito.when(OpenBankingConfigParser.getInstance().getCibaAuthenticationRedirectEndpoint()) + .thenReturn("testURL"); + OBCibaResponseTypeHandler obCibaResponseTypeHandler = new OBCibaResponseTypeHandler(); + OAuthAuthzReqMessageContext oAuthAuthzReqMessageContext = mock(OAuthAuthzReqMessageContext.class); + AuthenticatedUser cibaAuthenticatedUser = mock(AuthenticatedUser.class); + OAuth2AuthorizeReqDTO authorizationReqDTO = mock(OAuth2AuthorizeReqDTO.class); + doReturn(authorizationReqDTO).when(oAuthAuthzReqMessageContext).getAuthorizationReqDTO(); + doReturn(cibaAuthenticatedUser).when(authorizationReqDTO).getUser(); + ArrayList consentIds = new ArrayList<>(); + consentIds.add("test1"); + doReturn(consentIds).when(consentCoreServiceMock) + .getConsentIdByConsentAttributeNameAndValue(anyString(), anyString()); + DetailedConsentResource consentResourceMock = mock(DetailedConsentResource.class); + doReturn(consentResourceMock).when(consentCoreServiceMock).getDetailedConsent(anyString()); + doReturn("authorised").when(consentResourceMock).getCurrentStatus(); + + CibaMgtDAO cibaMgtDAO = mock(CibaMgtDAO.class); + doReturn(cibaMgtDAO).when(cibaDAOFactory).getCibaAuthMgtDAO(); + doReturn("authCode").when(cibaMgtDAO).getCibaAuthCodeKey(anyString()); + obCibaResponseTypeHandler.issue(oAuthAuthzReqMessageContext); + + } + + @Test(expectedExceptions = IdentityOAuth2Exception.class) + public void obCibaResponseTypeHandlerInValidCallbackTest() throws Exception { + + PowerMockito.when(OpenBankingConfigParser.getInstance().getCibaAuthenticationRedirectEndpoint()) + .thenReturn(""); + OBCibaResponseTypeHandler obCibaResponseTypeHandler = new OBCibaResponseTypeHandler(); + OAuthAuthzReqMessageContext oAuthAuthzReqMessageContext = mock(OAuthAuthzReqMessageContext.class); + AuthenticatedUser cibaAuthenticatedUser = mock(AuthenticatedUser.class); + OAuth2AuthorizeReqDTO authorizationReqDTO = mock(OAuth2AuthorizeReqDTO.class); + doReturn(authorizationReqDTO).when(oAuthAuthzReqMessageContext).getAuthorizationReqDTO(); + doReturn(cibaAuthenticatedUser).when(authorizationReqDTO).getUser(); + ArrayList consentIds = new ArrayList<>(); + consentIds.add("test1"); + doReturn(consentIds).when(consentCoreServiceMock) + .getConsentIdByConsentAttributeNameAndValue(anyString(), anyString()); + DetailedConsentResource consentResourceMock = mock(DetailedConsentResource.class); + doReturn(consentResourceMock).when(consentCoreServiceMock).getDetailedConsent(anyString()); + doReturn("authorised").when(consentResourceMock).getCurrentStatus(); + + CibaMgtDAO cibaMgtDAO = mock(CibaMgtDAO.class); + doReturn(cibaMgtDAO).when(cibaDAOFactory).getCibaAuthMgtDAO(); + doReturn("authCode").when(cibaMgtDAO).getCibaAuthCodeKey(anyString()); + obCibaResponseTypeHandler.issue(oAuthAuthzReqMessageContext); + + } + + @Test(expectedExceptions = IdentityOAuth2Exception.class) + public void obCibaResponseTypeHandlerInValidTest() throws Exception { + + PowerMockito.when(OpenBankingConfigParser.getInstance().getCibaAuthenticationRedirectEndpoint()) + .thenReturn("test"); + OBCibaResponseTypeHandler obCibaResponseTypeHandler = new OBCibaResponseTypeHandler(); + OAuthAuthzReqMessageContext oAuthAuthzReqMessageContext = mock(OAuthAuthzReqMessageContext.class); + AuthenticatedUser cibaAuthenticatedUser = mock(AuthenticatedUser.class); + OAuth2AuthorizeReqDTO authorizationReqDTO = mock(OAuth2AuthorizeReqDTO.class); + doReturn(authorizationReqDTO).when(oAuthAuthzReqMessageContext).getAuthorizationReqDTO(); + doReturn(cibaAuthenticatedUser).when(authorizationReqDTO).getUser(); + ArrayList consentIds = new ArrayList<>(); + consentIds.add("test1"); + doReturn(consentIds).when(consentCoreServiceMock) + .getConsentIdByConsentAttributeNameAndValue(anyString(), anyString()); + DetailedConsentResource consentResourceMock = mock(DetailedConsentResource.class); + doReturn(consentResourceMock).when(consentCoreServiceMock).getDetailedConsent(anyString()); + doReturn("authorised").when(consentResourceMock).getCurrentStatus(); + + CibaMgtDAO cibaMgtDAO = mock(CibaMgtDAO.class); + doReturn(cibaMgtDAO).when(cibaDAOFactory).getCibaAuthMgtDAO(); + doThrow(new CibaCoreException("")).when(cibaMgtDAO).getCibaAuthCodeKey(anyString()); + obCibaResponseTypeHandler.issue(oAuthAuthzReqMessageContext); + + } + + @Test(expectedExceptions = IdentityOAuth2Exception.class) + public void obCibaResponseTypeHandlerConsentManagementErrorTest() throws Exception { + + PowerMockito.when(OpenBankingConfigParser.getInstance().getCibaAuthenticationRedirectEndpoint()) + .thenReturn("test"); + OBCibaResponseTypeHandler obCibaResponseTypeHandler = new OBCibaResponseTypeHandler(); + OAuthAuthzReqMessageContext oAuthAuthzReqMessageContext = mock(OAuthAuthzReqMessageContext.class); + AuthenticatedUser cibaAuthenticatedUser = mock(AuthenticatedUser.class); + OAuth2AuthorizeReqDTO authorizationReqDTO = mock(OAuth2AuthorizeReqDTO.class); + doReturn(authorizationReqDTO).when(oAuthAuthzReqMessageContext).getAuthorizationReqDTO(); + doReturn(cibaAuthenticatedUser).when(authorizationReqDTO).getUser(); + ArrayList consentIds = new ArrayList<>(); + consentIds.add("test1"); + doReturn(consentIds).when(consentCoreServiceMock) + .getConsentIdByConsentAttributeNameAndValue(anyString(), anyString()); + DetailedConsentResource consentResourceMock = mock(DetailedConsentResource.class); + doThrow(new ConsentManagementException("")).when(consentCoreServiceMock).getDetailedConsent(anyString()); + doReturn("authorised").when(consentResourceMock).getCurrentStatus(); + CibaMgtDAO cibaMgtDAO = mock(CibaMgtDAO.class); + doReturn(cibaMgtDAO).when(cibaDAOFactory).getCibaAuthMgtDAO(); + obCibaResponseTypeHandler.issue(oAuthAuthzReqMessageContext); + + } +} diff --git a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.identity/src/test/resources/testng.xml b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.identity/src/test/resources/testng.xml index 0b21f929..b0a1a9f9 100644 --- a/open-banking-accelerator/components/com.wso2.openbanking.accelerator.identity/src/test/resources/testng.xml +++ b/open-banking-accelerator/components/com.wso2.openbanking.accelerator.identity/src/test/resources/testng.xml @@ -86,6 +86,7 @@ + diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/pom.xml b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/pom.xml index 1804ab40..8d8f24a8 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/pom.xml +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/pom.xml @@ -103,6 +103,11 @@ com.wso2.openbanking.accelerator com.wso2.openbanking.accelerator.event.notifications.service + + org.wso2.carbon.identity.event.handler.notification + org.wso2.carbon.identity.event.handler.notification + provided + diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/CIBAConsentPersistStep.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/CIBAConsentPersistStep.java index a7062374..a9ad7927 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/CIBAConsentPersistStep.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/CIBAConsentPersistStep.java @@ -15,21 +15,28 @@ * specific language governing permissions and limitations * under the License. */ - - package com.wso2.openbanking.accelerator.consent.extensions.authorize.impl; +package com.wso2.openbanking.accelerator.consent.extensions.authorize.impl; + +import com.wso2.openbanking.accelerator.common.constant.OpenBankingConstants; import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; import com.wso2.openbanking.accelerator.consent.extensions.authorize.model.ConsentData; import com.wso2.openbanking.accelerator.consent.extensions.authorize.model.ConsentPersistData; import com.wso2.openbanking.accelerator.consent.extensions.authorize.model.ConsentPersistStep; import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentException; +import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentExtensionConstants; +import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentExtensionUtils; import com.wso2.openbanking.accelerator.consent.extensions.common.ResponseStatus; import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; +import com.wso2.openbanking.accelerator.consent.mgt.dao.models.ConsentAttributes; import com.wso2.openbanking.accelerator.consent.mgt.dao.models.ConsentResource; import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Optional; /** * Consent persistence step for CIBA flow. @@ -54,16 +61,49 @@ public void execute(ConsentPersistData consentPersistData) throws ConsentExcepti if (sensitiveDataMap != null) { //Storing mapping to be used to bind consent id scope for CIBA flows - if (sensitiveDataMap.containsKey("auth_req_id")) { - Map consentAttributes = new HashMap<>(); - consentAttributes.put("auth_req_id", (String) consentData.getSensitiveDataMap().get("auth_req_id")); - ConsentExtensionsDataHolder.getInstance().getConsentCoreService().storeConsentAttributes - (consentResource.getConsentID(), consentAttributes); + if (sensitiveDataMap.containsKey(OpenBankingConstants.AUTH_REQ_ID)) { + removeExistingAuthReqIdAttribute(consentResource); + storeAuthReqIdAttribute((String) sensitiveDataMap.get(OpenBankingConstants.AUTH_REQ_ID), + consentResource); + } else if (ConsentExtensionUtils.isCibaWebAuthLinkFlow(consentData) + && sensitiveDataMap.containsKey(ConsentExtensionConstants.SP_QUERY_PARAMS)) { + String spQueryParams = sensitiveDataMap.get(ConsentExtensionConstants.SP_QUERY_PARAMS).toString(); + Optional nonce = Arrays.stream(spQueryParams.split("&")) + .filter(e -> e.startsWith(ConsentExtensionConstants.NONCE)).findFirst() + .map(e -> e.split("=")[1]); + if (nonce.isPresent()) { + removeExistingAuthReqIdAttribute(consentResource); + storeAuthReqIdAttribute(nonce.get(), consentResource); + } + } } } catch (ConsentManagementException e) { throw new ConsentException(ResponseStatus.INTERNAL_SERVER_ERROR, - "Exception occured while persisting consent"); + "Exception occurred while persisting consent"); + } + } + + private static void storeAuthReqIdAttribute(String authReqId, ConsentResource consentResource) + throws ConsentManagementException { + + Map consentAttributes = new HashMap<>(); + consentAttributes.put(OpenBankingConstants.AUTH_REQ_ID, authReqId); + ConsentExtensionsDataHolder.getInstance().getConsentCoreService().storeConsentAttributes + (consentResource.getConsentID(), consentAttributes); + } + + private static void removeExistingAuthReqIdAttribute(ConsentResource consentResource) + throws ConsentManagementException { + + ConsentAttributes currentConsentAttributes = ConsentExtensionsDataHolder.getInstance() + .getConsentCoreService().getConsentAttributes(consentResource.getConsentID()); + // Remove if existing aut_req_id is already in attributes. + if (currentConsentAttributes.getConsentAttributes().containsKey(OpenBankingConstants.AUTH_REQ_ID)) { + ArrayList toRemoveAttributes = new ArrayList<>(); + toRemoveAttributes.add(OpenBankingConstants.AUTH_REQ_ID); + ConsentExtensionsDataHolder.getInstance().getConsentCoreService().deleteConsentAttributes( + consentResource.getConsentID(), toRemoveAttributes); } } } diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/CIBAWebAuthLinkConsentPersistStep.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/CIBAWebAuthLinkConsentPersistStep.java new file mode 100644 index 00000000..caa2d8e5 --- /dev/null +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/CIBAWebAuthLinkConsentPersistStep.java @@ -0,0 +1,172 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 com.wso2.openbanking.accelerator.consent.extensions.authorize.impl; + +import com.wso2.openbanking.accelerator.common.constant.OpenBankingConstants; +import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; +import com.wso2.openbanking.accelerator.consent.extensions.authorize.model.ConsentData; +import com.wso2.openbanking.accelerator.consent.extensions.authorize.model.ConsentPersistData; +import com.wso2.openbanking.accelerator.consent.extensions.authorize.model.ConsentPersistStep; +import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentException; +import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentExtensionConstants; +import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentExtensionUtils; +import com.wso2.openbanking.accelerator.consent.extensions.common.ResponseStatus; +import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; +import com.wso2.openbanking.accelerator.consent.mgt.dao.models.AuthorizationResource; +import com.wso2.openbanking.accelerator.consent.mgt.dao.models.ConsentResource; +import com.wso2.openbanking.accelerator.consent.mgt.dao.models.DetailedConsentResource; +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Consent persistence step for CIBA web auth links flow + */ +public class CIBAWebAuthLinkConsentPersistStep implements ConsentPersistStep { + + private static final Log log = LogFactory.getLog(CIBAWebAuthLinkConsentPersistStep.class); + + @Override + public void execute(ConsentPersistData consentPersistData) throws ConsentException { + + try { + if (!ConsentExtensionUtils.isCibaWebAuthLinkFlow(consentPersistData.getConsentData())) { + // Enabling execution for CIBA web auth link flows. + return; + } + bindUserToConsent(consentPersistData); + } catch (ConsentManagementException e) { + throw new ConsentException(ResponseStatus.INTERNAL_SERVER_ERROR, + "Exception occurred while persisting consent"); + } + } + + /** + * Method to update authenticated state / accountIds of the user to the authorisation resources. + * + * @param consentPersistData ConsentPersistData + * @throws ConsentManagementException exception + */ + protected void bindUserToConsent(ConsentPersistData consentPersistData) throws ConsentManagementException { + + ConsentData consentData = consentPersistData.getConsentData(); + if (consentData.getConsentId() == null && consentData.getConsentResource() == null) { + log.error("Consent ID not available in consent data"); + throw new ConsentException(ResponseStatus.INTERNAL_SERVER_ERROR, + "Consent ID not available in consent data"); + } + DetailedConsentResource detailedConsent = ConsentExtensionsDataHolder.getInstance().getConsentCoreService() + .getDetailedConsent(consentData.getConsentId()); + ConsentResource consentResource = new ConsentResource(detailedConsent.getConsentID(), + detailedConsent.getClientID(), detailedConsent.getReceipt(), detailedConsent.getConsentType(), + detailedConsent.getConsentFrequency(), detailedConsent.getValidityPeriod(), + detailedConsent.isRecurringIndicator(), detailedConsent.getCurrentStatus(), + detailedConsent.getCreatedTime(), detailedConsent.getUpdatedTime()); + + if (!(consentResource.getCurrentStatus().equals(OpenBankingConstants.AWAITING_AUTHORISATION_STATUS) || + consentResource.getCurrentStatus().equals( + OpenBankingConstants.AWAITING_FURTHER_AUTHORISATION_STATUS))) { + log.error("Consent not in authorizable state"); + throw new ConsentException(ResponseStatus.BAD_REQUEST, "Consent not in authorizable state"); + } + + ArrayList authorizationResources = detailedConsent.getAuthorizationResources(); + List userAuthorizationResources = authorizationResources.stream() + .filter(e -> consentData.getUserId().equalsIgnoreCase(e.getUserID())) + .collect(Collectors.toList()); + + if (userAuthorizationResources.size() != 1) { + log.error("User's auth resource is not available or multiple entries found for the given consent"); + throw new ConsentException(ResponseStatus.BAD_REQUEST, + "User's auth resource is not available or multiple entries found for the given consent"); + } + + AuthorizationResource userAuthorizationResource = userAuthorizationResources.get(0); + if (!userAuthorizationResource.getAuthorizationStatus().equalsIgnoreCase( + OpenBankingConstants.CREATED_AUTHORISATION_RESOURCE_STATE)) { + log.error("User auth resource is not in authorizable state"); + throw new ConsentException(ResponseStatus.BAD_REQUEST, "User auth resource is not in authorizable state"); + } + + JSONObject payload = consentPersistData.getPayload(); + if (payload.get("accountIds") == null || !(payload.get("accountIds") instanceof JSONArray)) { + log.error("Account IDs not available in persist request"); + throw new ConsentException(ResponseStatus.BAD_REQUEST, + "Account IDs not available in persist request"); + } + JSONArray accountIds = (JSONArray) payload.get("accountIds"); + ArrayList accountIdsString = new ArrayList<>(); + for (Object account : accountIds) { + if (!(account instanceof String)) { + log.error("Account IDs format error in persist request"); + throw new ConsentException(ResponseStatus.BAD_REQUEST, + "Account IDs format error in persist request"); + } + accountIdsString.add((String) account); + } + + String authStatus; + if (consentPersistData.getApproval()) { + authStatus = ConsentExtensionConstants.AUTHORISED_STATUS; + userAuthorizationResource.setAuthorizationStatus(authStatus); + } else { + authStatus = ConsentExtensionConstants.REJECTED_STATUS; + userAuthorizationResource.setAuthorizationStatus(authStatus); + } + + // Check existing authorisation resources entries. + List rejectedAuthResources = authorizationResources.stream() + .filter(e -> e.getAuthorizationStatus().equals(ConsentExtensionConstants.REJECTED_STATUS)) + .collect(Collectors.toList()); + + List createdAuthResources = authorizationResources.stream() + .filter(e -> e.getAuthorizationStatus().equals( + OpenBankingConstants.CREATED_AUTHORISATION_RESOURCE_STATE)) + .collect(Collectors.toList()); + + List authorisedAuthResources = authorizationResources.stream() + .filter(e -> e.getAuthorizationStatus().equals(ConsentExtensionConstants.AUTHORISED_STATUS)) + .collect(Collectors.toList()); + + if (!rejectedAuthResources.isEmpty()) { + ConsentExtensionsDataHolder.getInstance().getConsentCoreService().bindUserAccountsToConsent( + consentResource, consentData.getUserId(), userAuthorizationResource.getAuthorizationID(), + accountIdsString, authStatus, OpenBankingConstants.REJECTED_STATUS); + } else if (!createdAuthResources.isEmpty() && authorisedAuthResources.isEmpty()) { + ConsentExtensionsDataHolder.getInstance().getConsentCoreService().bindUserAccountsToConsent( + consentResource, consentData.getUserId(), userAuthorizationResource.getAuthorizationID(), + accountIdsString, authStatus, OpenBankingConstants.AWAITING_AUTHORISATION_STATUS); + } else if (!createdAuthResources.isEmpty()) { + ConsentExtensionsDataHolder.getInstance().getConsentCoreService().bindUserAccountsToConsent( + consentResource, consentData.getUserId(), userAuthorizationResource.getAuthorizationID(), + accountIdsString, authStatus, OpenBankingConstants.AWAITING_FURTHER_AUTHORISATION_STATUS); + } else if (!authorisedAuthResources.isEmpty()) { + ConsentExtensionsDataHolder.getInstance().getConsentCoreService().bindUserAccountsToConsent( + consentResource, consentData.getUserId(), userAuthorizationResource.getAuthorizationID(), + accountIdsString, authStatus, ConsentExtensionConstants.AUTHORISED_STATUS); + } + + } + +} diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/DefaultConsentPersistStep.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/DefaultConsentPersistStep.java index 49760439..5c9b63fb 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/DefaultConsentPersistStep.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/DefaultConsentPersistStep.java @@ -26,6 +26,7 @@ import com.wso2.openbanking.accelerator.consent.extensions.authorize.model.ConsentPersistStep; import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentException; import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentExtensionConstants; +import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentExtensionUtils; import com.wso2.openbanking.accelerator.consent.extensions.common.ResponseStatus; import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; import com.wso2.openbanking.accelerator.consent.mgt.dao.models.ConsentResource; @@ -48,6 +49,12 @@ public class DefaultConsentPersistStep implements ConsentPersistStep { public void execute(ConsentPersistData consentPersistData) throws ConsentException { try { + + if (ConsentExtensionUtils.isCibaWebAuthLinkFlow(consentPersistData.getConsentData())) { + // Skipping execution for CIBA web auth link flows. + return; + } + ConsentData consentData = consentPersistData.getConsentData(); ConsentResource consentResource; diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/DefaultConsentRetrievalStep.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/DefaultConsentRetrievalStep.java index 0deca7b5..66cdab13 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/DefaultConsentRetrievalStep.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/impl/DefaultConsentRetrievalStep.java @@ -18,12 +18,14 @@ package com.wso2.openbanking.accelerator.consent.extensions.authorize.impl; +import com.wso2.openbanking.accelerator.common.constant.OpenBankingConstants; import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; import com.wso2.openbanking.accelerator.consent.extensions.authorize.model.ConsentData; import com.wso2.openbanking.accelerator.consent.extensions.authorize.model.ConsentRetrievalStep; import com.wso2.openbanking.accelerator.consent.extensions.authorize.utils.ConsentRetrievalUtil; import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentException; import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentExtensionConstants; +import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentExtensionUtils; import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentServiceUtil; import com.wso2.openbanking.accelerator.consent.extensions.common.ResponseStatus; import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; @@ -35,6 +37,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import java.util.ArrayList; + /** * Consent retrieval step default implementation. */ @@ -67,7 +71,9 @@ public void execute(ConsentData consentData, JSONObject jsonObject) throws Conse } ConsentResource consentResource = consentCoreService.getConsent(consentId, false); - if (!consentResource.getCurrentStatus().equals(ConsentExtensionConstants.AWAITING_AUTH_STATUS)) { + if (!(consentResource.getCurrentStatus().equals(OpenBankingConstants.AWAITING_AUTHORISATION_STATUS) || + consentResource.getCurrentStatus().equals( + OpenBankingConstants.AWAITING_FURTHER_AUTHORISATION_STATUS))) { log.error("Consent not in authorizable state"); //Currently throwing error as 400 response. Developer also have the option of appending a field IS_ERROR // to the jsonObject and showing it to the user in the webapp. If so, the IS_ERROR have to be checked in @@ -75,9 +81,18 @@ public void execute(ConsentData consentData, JSONObject jsonObject) throws Conse throw new ConsentException(ResponseStatus.BAD_REQUEST, "Consent not in authorizable state"); } - AuthorizationResource authorizationResource = ConsentExtensionsDataHolder.getInstance() - .getConsentCoreService().searchAuthorizations(consentId).get(0); - if (!authorizationResource.getAuthorizationStatus().equals(ConsentExtensionConstants.CREATED_STATUS)) { + AuthorizationResource authorizationResource; + + if (ConsentExtensionUtils.isCibaWebAuthLinkFlow(consentData)) { + ArrayList authorizationResources = ConsentExtensionsDataHolder.getInstance() + .getConsentCoreService().searchAuthorizations(consentId, consentData.getUserId()); + authorizationResource = authorizationResources.size() == 1 ? authorizationResources.get(0) : null; + } else { + authorizationResource = ConsentExtensionsDataHolder.getInstance() + .getConsentCoreService().searchAuthorizations(consentId).get(0); + } + + if (authorizationResource == null || !authorizationResource.getAuthorizationStatus().equals("created")) { log.error("Authorization not in authorizable state"); //Currently throwing error as 400 response. Developer also have the option of appending a field IS_ERROR // to the jsonObject and showing it to the user in the webapp. If so, the IS_ERROR have to be checked in diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/CIBAWebLinkAuthenticator.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/CIBAWebLinkAuthenticator.java new file mode 100644 index 00000000..dfc90311 --- /dev/null +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/CIBAWebLinkAuthenticator.java @@ -0,0 +1,268 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink; + + +import com.nimbusds.jwt.SignedJWT; +import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; +import com.wso2.openbanking.accelerator.common.constant.OpenBankingConstants; +import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; +import com.wso2.openbanking.accelerator.common.util.CarbonUtils; +import com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.CIBAPushAuthenticatorConstants; +import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; +import com.wso2.openbanking.accelerator.consent.mgt.dao.models.AuthorizationResource; +import com.wso2.openbanking.accelerator.consent.mgt.service.ConsentCoreService; +import net.minidev.json.JSONObject; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.extension.identity.helper.FederatedAuthenticatorUtil; +import org.wso2.carbon.identity.application.authentication.framework.AbstractApplicationAuthenticator; +import org.wso2.carbon.identity.application.authentication.framework.FederatedApplicationAuthenticator; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException; +import org.wso2.carbon.identity.application.authentication.framework.inbound.InboundConstants; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; +import org.wso2.carbon.identity.event.IdentityEventConstants; +import org.wso2.carbon.identity.event.IdentityEventException; +import org.wso2.carbon.identity.event.event.Event; +import org.wso2.carbon.user.api.UserStoreException; + +import java.text.ParseException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * CIBA Web Link Authenticator for sending auth web links to authentication device / devices + */ +public class CIBAWebLinkAuthenticator extends AbstractApplicationAuthenticator + implements FederatedApplicationAuthenticator { + + private static final Log log = LogFactory.getLog(CIBAWebLinkAuthenticator.class); + private static final ConsentCoreService consentCoreService = + ConsentExtensionsDataHolder.getInstance().getConsentCoreService(); + private static final String AUTHORIZE_URL_PATH = "/oauth2/authorize?"; + + @Override + protected void initiateAuthenticationRequest(HttpServletRequest request, HttpServletResponse response, + AuthenticationContext context) throws AuthenticationFailedException { + + List authenticatedUsers = getAuthenticatedUsers(request); + for (AuthenticatedUser user : authenticatedUsers) { + try { + if (!FederatedAuthenticatorUtil.isUserExistInUserStore(user.getUserName())) { + log.error(String.format("User does not exist in the User store : %s", user.getUserName())); + throw new AuthenticationFailedException("User does not exist in the User store"); + } + } catch (UserStoreException e) { + log.error(String.format("Cannot find the user in User store : %s", user.getUserName())); + throw new AuthenticationFailedException("Cannot find the user in User store", e); + } + } + // Handle creating auth resources for users. + createAuthResourcesForUsers(authenticatedUsers, context); + HashMap webAuthLinksMap = new HashMap<>(); + for (AuthenticatedUser user : authenticatedUsers) { + webAuthLinksMap.put(user.getUserName(), generateWebAuthLink(context, user)); + } + if (log.isDebugEnabled()) { + log.debug(String.format("%s no. of users has been resolved for web auth links", + authenticatedUsers.size())); + } + + // Triggering notification event for web links + for (Map.Entry webLinkEntry : webAuthLinksMap.entrySet()) { + String webAuthLink = webAuthLinksMap.get(webLinkEntry.getKey()); + String username = webLinkEntry.getKey(); + triggerNotificationEvent(username, webAuthLink); + } + + } + + /** + * Method to create authorisation resources for given users. + * + * @param authenticatedUsers list of users involved for the authorisation + * @param context authenticationContext + */ + protected void createAuthResourcesForUsers(List authenticatedUsers, + AuthenticationContext context) throws AuthenticationFailedException { + + try { + // Extract consentId + Optional requestObject = Arrays.stream(context.getQueryParams().split("&")) + .filter(e -> e.startsWith(CIBAWebLinkAuthenticatorConstants.REQUEST_OBJECT)).findFirst() + .map(e -> e.split("=")[1]); + + if (requestObject.isPresent()) { + SignedJWT signedJWT = SignedJWT.parse(requestObject.get()); + JSONObject claims = (JSONObject) signedJWT.getJWTClaimsSet() + .getClaim(CIBAWebLinkAuthenticatorConstants.CLAIMS); + JSONObject userinfo = (JSONObject) claims.get(CIBAWebLinkAuthenticatorConstants.USER_INFO); + JSONObject openBankingIntentId = + (JSONObject) userinfo.get(CIBAWebLinkAuthenticatorConstants.OPEN_BANKING_INTENT_ID); + String consentId = (String) openBankingIntentId.get(CIBAWebLinkAuthenticatorConstants.VALUE); + + // Extract usernames + List usernames = authenticatedUsers.stream() + .map(AuthenticatedUser::getUserName) + .map(username -> username.endsWith(OpenBankingConstants.CARBON_SUPER_TENANT_DOMAIN) ? + username : username + OpenBankingConstants.CARBON_SUPER_TENANT_DOMAIN) + .collect(Collectors.toList()); + + List consentAuthResources = + consentCoreService.searchAuthorizations(consentId); + if (consentAuthResources.size() == 1 && consentAuthResources.get(0).getAuthorizationStatus() + .equals(OpenBankingConstants.CREATED_AUTHORISATION_RESOURCE_STATE)) { + // Scenario : Initiated consent with default auth resources binding. + consentCoreService.updateAuthorizationUser(consentAuthResources.get(0).getAuthorizationID(), + usernames.get(0)); + for (int i = 1; i < usernames.size(); i++) { + AuthorizationResource userAuthResource = new AuthorizationResource(consentId, + usernames.get(i), OpenBankingConstants.CREATED_AUTHORISATION_RESOURCE_STATE, + OpenBankingConstants.MULTI_AUTH_AUTHORISATION_TYPE, System.currentTimeMillis()); + consentCoreService.createConsentAuthorization(userAuthResource); + } + } else if (consentAuthResources.size() == authenticatedUsers.size()) { + for (AuthorizationResource authorizationResource : consentAuthResources) { + if (!usernames.contains(authorizationResource.getUserID())) { + log.error("No matching authorisation resources found for the given consent."); + throw new AuthenticationFailedException("No matching authorisation resources found for the " + + "given consent."); + } + } + } else { + log.error("Authorisation resources partially exists for the given consent."); + throw new AuthenticationFailedException("Authorisation resources partially exists for the " + + "given consent."); + } + } else { + throw new AuthenticationFailedException("Could not extract request object from the request."); + } + } catch (ConsentManagementException | ParseException e) { + log.error("Error occurred while persisting authorisation resources", e); + throw new AuthenticationFailedException("Error occurred while persisting authorisation resources", e); + } + } + + /** + * Method to trigger the notification event in IS. + */ + protected void triggerNotificationEvent(String userName, String webLink) throws AuthenticationFailedException { + + HashMap properties = new HashMap<>(); + properties.put(IdentityEventConstants.EventProperty.USER_NAME, userName); + properties.put(OpenBankingConstants.CIBA_WEB_AUTH_LINK_PARAM, webLink); + Event identityMgtEvent = new Event(CIBAWebLinkAuthenticatorConstants.NOTIFICATION_TRIGGER_EVENT, properties); + try { + ConsentExtensionsDataHolder.getInstance().getIdentityEventService().handleEvent(identityMgtEvent); + } catch (IdentityEventException e) { + throw new AuthenticationFailedException("Error occurred while calling triggerNotificationEvent", e); + } + + } + + /** + * Method to identify the user/users involved in the authentication. + * + * @param request HttpServletRequest + * @return list of users + */ + protected List getAuthenticatedUsers(HttpServletRequest request) { + + List users = Arrays.stream(request.getParameter(CIBAPushAuthenticatorConstants.LOGIN_HINT) + .split(",")) + .map(String::trim) + .map(AuthenticatedUser::createLocalAuthenticatedUserFromSubjectIdentifier) + .collect(Collectors.toList()); + return users; + } + + /** + * Method to generate web auth links for given user. + * + * @param context authentication context. + * @param user authenticated user. + * @return Auth web link for authenticated user. + */ + protected String generateWebAuthLink(AuthenticationContext context, AuthenticatedUser user) { + + List allowedParams = OpenBankingConfigParser.getInstance().getCibaWebLinkAllowedParams(); + List paramList = Arrays.stream(context.getQueryParams().split("&")).filter(e -> { + for (String allowedParam : allowedParams) { + if (e.startsWith(allowedParam)) { + return true; + } + } + return false; + }).collect(Collectors.toList()); + + // Rename `request_object` query params to `request` param. + List requestObjectList = Arrays.stream(context.getQueryParams().split("&")) + .filter(e -> e.startsWith("request_object")).collect(Collectors.toList()); + String requestObject = requestObjectList.get(0).split("=")[1]; + paramList.add("request=" + requestObject); + paramList.add(OpenBankingConstants.CIBA_WEB_AUTH_LINK_PARAM + "=true"); + paramList.add("login_hint=" + user.getUserName()); + + StringBuilder builder = new StringBuilder(); + builder.append(CarbonUtils.getCarbonServerUrl()).append(AUTHORIZE_URL_PATH); + for (String param : paramList) { + builder.append(param).append("&"); + } + if (log.isDebugEnabled()) { + log.debug(builder.toString()); + } + return builder.toString(); + } + + + @Override + protected void processAuthenticationResponse(HttpServletRequest request, HttpServletResponse response, + AuthenticationContext context) throws AuthenticationFailedException { + // This authenticator is used only to send the web-auth links, And it does not expect to process the response. + } + + @Override + public boolean canHandle(HttpServletRequest request) { + // CIBA web link Authenticator is used only to send the web-auth links, And it does not expect to handle it. + return false; + } + + @Override + public String getContextIdentifier(HttpServletRequest request) { + return request.getParameter(InboundConstants.RequestProcessor.CONTEXT_KEY); + } + + @Override + public String getName() { + return CIBAWebLinkAuthenticatorConstants.AUTHENTICATOR_NAME; + } + + @Override + public String getFriendlyName() { + return CIBAWebLinkAuthenticatorConstants.AUTHENTICATOR_FRIENDLY_NAME; + } +} diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/CIBAWebLinkAuthenticatorConstants.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/CIBAWebLinkAuthenticatorConstants.java new file mode 100644 index 00000000..cbca713a --- /dev/null +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/CIBAWebLinkAuthenticatorConstants.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink; + +/** + * CIBA web link authenticator Constants + */ +public class CIBAWebLinkAuthenticatorConstants { + + public static final String AUTHENTICATOR_NAME = "ciba-weblink"; + public static final String AUTHENTICATOR_FRIENDLY_NAME = "CIBA Web Link Authenticator"; + public static final String NOTIFICATION_TRIGGER_EVENT = "CIBA_WEBLINK_NOTIFICATION_EVENT"; + public static final String NOTIFICATION_HANDLER_NAME = "cibaWebLinkNotificationHandler"; + public static final String REQUEST_OBJECT = "request_object"; + public static final String OPEN_BANKING_INTENT_ID = "openbanking_intent_id"; + public static final String VALUE = "value"; + public static final String USER_INFO = "userinfo"; + public static final String CLAIMS = "claims"; + +} diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/notification/CIBAWebLinkNotificationHandler.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/notification/CIBAWebLinkNotificationHandler.java new file mode 100644 index 00000000..1ca47ef6 --- /dev/null +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/notification/CIBAWebLinkNotificationHandler.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink.notification; + +import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; +import com.wso2.openbanking.accelerator.common.constant.OpenBankingConstants; +import com.wso2.openbanking.accelerator.common.exception.OpenBankingException; +import com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink.CIBAWebLinkAuthenticatorConstants; +import com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink.notification.provider.NotificationProvider; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.base.IdentityRuntimeException; +import org.wso2.carbon.identity.core.bean.context.MessageContext; +import org.wso2.carbon.identity.event.IdentityEventConstants; +import org.wso2.carbon.identity.event.IdentityEventException; +import org.wso2.carbon.identity.event.bean.IdentityEventMessageContext; +import org.wso2.carbon.identity.event.event.Event; +import org.wso2.carbon.identity.event.handler.notification.DefaultNotificationHandler; + +import java.lang.reflect.InvocationTargetException; + + +/** + * This class represents the ciba web link notification handler. + */ +public class CIBAWebLinkNotificationHandler extends DefaultNotificationHandler { + + private static final Log log = LogFactory.getLog(CIBAWebLinkNotificationHandler.class); + private static NotificationProvider notificationProvider; + + public CIBAWebLinkNotificationHandler() { + setCIBAWebLinkNotificationProvider(); + } + + @Override + public boolean canHandle(MessageContext messageContext) throws IdentityRuntimeException { + + Event event = ((IdentityEventMessageContext) messageContext).getEvent(); + return event.getEventName().equals(CIBAWebLinkAuthenticatorConstants.NOTIFICATION_TRIGGER_EVENT); + } + + @Override + public String getName() { + + return CIBAWebLinkAuthenticatorConstants.NOTIFICATION_HANDLER_NAME; + } + + @Override + public void handleEvent(Event event) throws IdentityEventException { + + String username = (String) event.getEventProperties().get(IdentityEventConstants.EventProperty.USER_NAME); + String weblink = (String) event.getEventProperties().get(OpenBankingConstants.CIBA_WEB_AUTH_LINK_PARAM); + + if (log.isDebugEnabled()) { + log.debug("Handling notification event : " + weblink); + } + + try { + notificationProvider.send(username, weblink); + } catch (OpenBankingException e) { + log.error("Error occurred while sending the notification", e); + } + + } + + /** + * Retrieve the config for CIBA notification provider + */ + private static void setCIBAWebLinkNotificationProvider() { + + try { + notificationProvider = (NotificationProvider) + Class.forName(OpenBankingConfigParser.getInstance() + .getCibaWebLinkNotificationProvider()).getDeclaredConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | + InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) { + log.error("CIBA notification provider extension not found", e); + } + } + +} diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/notification/provider/NotificationProvider.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/notification/provider/NotificationProvider.java new file mode 100644 index 00000000..ff892849 --- /dev/null +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/notification/provider/NotificationProvider.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink.notification.provider; + +import com.wso2.openbanking.accelerator.common.exception.OpenBankingException; + +/** + * This interface represents the CIBA notification provider. + */ +public interface NotificationProvider { + + /** + * Sends the notification using the provider. + * + * @param username username + * @param webLink weblink of the user + * @throws OpenBankingException + */ + void send(String username, String webLink) throws OpenBankingException; +} diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/notification/provider/SMSNotificationProvider.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/notification/provider/SMSNotificationProvider.java new file mode 100644 index 00000000..b88ec202 --- /dev/null +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/authenticator/weblink/notification/provider/SMSNotificationProvider.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink.notification.provider; + +import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; +import com.wso2.openbanking.accelerator.common.exception.OpenBankingException; +import com.wso2.openbanking.accelerator.common.util.HTTPClientUtils; +import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.user.api.UserRealm; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.utils.multitenancy.MultitenantUtils; + +import java.io.IOException; +import java.util.Map; + +/** + * This class implements the SMS based CIBA notification provider. + */ +public class SMSNotificationProvider implements NotificationProvider { + + public static final String MOBILE_CLAIM = "http://wso2.org/claims/mobile"; + + @Override + public void send(String username, String webLink) throws OpenBankingException { + + AuthenticatedUser authenticatedUser = + AuthenticatedUser.createFederateAuthenticatedUserFromSubjectIdentifier(username); + String mobileNumber = null; + try { + int tenantId = IdentityTenantUtil.getTenantIdOfUser(username); + UserRealm userRealm = ConsentExtensionsDataHolder.getInstance().getRealmService() + .getTenantUserRealm(tenantId); + Map claimValues = + userRealm.getUserStoreManager().getUserClaimValues( + MultitenantUtils.getTenantAwareUsername(authenticatedUser.toFullQualifiedUsername()), + new String[]{MOBILE_CLAIM}, null); + if (claimValues != null && claimValues.containsKey(MOBILE_CLAIM) && + !claimValues.get(MOBILE_CLAIM).isEmpty()) { + mobileNumber = claimValues.get(MOBILE_CLAIM); + } else { + throw new OpenBankingException(String.format( + "Error could not resolve mobile number for user : %s", username)); + } + } catch (UserStoreException e) { + throw new OpenBankingException(String.format( + "Error could not resolve mobile number for user : %s", username), e); + } + + try (CloseableHttpClient httpClient = HTTPClientUtils.getHttpsClient()) { + String notificationServiceURL = + OpenBankingConfigParser.getInstance().getCibaWebLinkSMSNotificationServiceURL(); + if (notificationServiceURL == null) { + throw new OpenBankingException("Error occurred while retrieving SMS service URL."); + } + HttpPost httpPost = new HttpPost(notificationServiceURL); + StringEntity entity = new StringEntity(getPayload(username, webLink, mobileNumber)); + httpPost.setEntity(entity); + setHeaders(httpPost); + CloseableHttpResponse httpResponse = httpClient.execute(httpPost); + + if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK || + httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_CREATED) { + String error = String.format("Error while invoking rest api : %s %s", + httpResponse.getStatusLine().getStatusCode(), httpResponse.getStatusLine().getReasonPhrase()); + throw new OpenBankingException(error); + } + } catch (IOException e) { + throw new OpenBankingException("Error when creating http client.", e); + } + } + + /** + * Method to set the http headers to the SMS request. + * + * @param httpPost httpPost + */ + public void setHeaders(HttpPost httpPost) { + Map notificationRequestHeaders = OpenBankingConfigParser.getInstance() + .getCibaWebLinkSMSNotificationRequestHeaders(); + for (Map.Entry header : notificationRequestHeaders.entrySet()) { + httpPost.setHeader(header.getKey(), header.getValue()); + } + } + + /** + * Method to get the payload of the SMS service request. + * + * @param username username + * @param webLink auth web link + * @return payload in string + */ + public String getPayload(String username, String webLink, String mobileNumber) { + // SMS message payload to be sent to SMS service. + return webLink; + } +} diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/ConsentExtensionConstants.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/ConsentExtensionConstants.java index 3da1a41e..0825053b 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/ConsentExtensionConstants.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/ConsentExtensionConstants.java @@ -57,6 +57,7 @@ public class ConsentExtensionConstants { "ReadTransactionsDetail", "ReadBalances"); public static final String DEFAULT_PERMISSION = "ReadPersonalDetail"; + public static final String NONCE = "nonce"; public static final String DATA = "Data"; public static final String INITIATION = "Initiation"; diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/ConsentExtensionUtils.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/ConsentExtensionUtils.java index 3588725c..9d28bb32 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/ConsentExtensionUtils.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/common/ConsentExtensionUtils.java @@ -50,17 +50,21 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Base64; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.Set; import javax.servlet.http.HttpServletRequest; +import static org.apache.oltu.oauth2.common.OAuth.OAUTH_RESPONSE_TYPE; + /** * Util class for consent extensions. */ @@ -434,4 +438,25 @@ public static JSONArray getDummyAccounts() { accountsJSON.add(accountTwo); return accountsJSON; } + + /** + * Method to check if CIBA SMS web auth link based flow. + * + * @param consentData ConsentData + * @return Boolean + */ + public static boolean isCibaWebAuthLinkFlow(ConsentData consentData) { + Optional responseTypeParam = Arrays.stream(consentData.getSpQueryParams().split("&")) + .filter(e -> e.startsWith(OAUTH_RESPONSE_TYPE)).findFirst(); + Optional cibaWebAuthLinkParam = Arrays.stream(consentData.getSpQueryParams().split("&")) + .filter(e -> e.startsWith(OpenBankingConstants.CIBA_WEB_AUTH_LINK_PARAM)).findFirst(); + String responseTypeValue = ""; + boolean isCIBAWebLink = false; + if (responseTypeParam.isPresent() && cibaWebAuthLinkParam.isPresent()) { + responseTypeValue = responseTypeParam.get().split("=")[1]; + isCIBAWebLink = Boolean.parseBoolean(cibaWebAuthLinkParam.get().split("=")[1]); + + } + return responseTypeValue.equals(OpenBankingConstants.CIBA_AUTH_CODE_RESPONSE_TYPE) && isCIBAWebLink; + } } diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/internal/ConsentExtensionsComponent.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/internal/ConsentExtensionsComponent.java index 0c9e6e95..cf95f2c0 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/internal/ConsentExtensionsComponent.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/internal/ConsentExtensionsComponent.java @@ -20,6 +20,8 @@ import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigurationService; import com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.CIBAPushAuthenticator; +import com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink.CIBAWebLinkAuthenticator; +import com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink.notification.CIBAWebLinkNotificationHandler; import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentExtensionExporter; import com.wso2.openbanking.accelerator.consent.extensions.util.PeriodicalConsentJobActivator; import com.wso2.openbanking.accelerator.consent.mgt.service.ConsentCoreService; @@ -33,6 +35,9 @@ import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; import org.wso2.carbon.identity.application.authentication.framework.ApplicationAuthenticator; +import org.wso2.carbon.identity.event.handler.AbstractEventHandler; +import org.wso2.carbon.identity.event.services.IdentityEventService; +import org.wso2.carbon.user.core.service.RealmService; /** * The Component class for activating consent extensions osgi service. @@ -56,8 +61,13 @@ protected void activate(ComponentContext context) { log.debug("Periodical Consent Status Updater Started"); } CIBAPushAuthenticator authenticator = new CIBAPushAuthenticator(); + CIBAWebLinkAuthenticator cibaWebLinkAuthenticator = new CIBAWebLinkAuthenticator(); context.getBundleContext().registerService(ApplicationAuthenticator.class.getName(), authenticator, null); + context.getBundleContext().registerService(ApplicationAuthenticator.class.getName(), + cibaWebLinkAuthenticator, null); + context.getBundleContext().registerService(AbstractEventHandler.class.getName(), + new CIBAWebLinkNotificationHandler(), null); if (log.isDebugEnabled()) { log.debug("CIBA Push authenticator bundle is activated"); } @@ -105,4 +115,39 @@ public void unsetConsentCoreService(ConsentCoreService consentCoreService) { ConsentExtensionsDataHolder.getInstance().setConsentCoreService(null); } + + @Reference( + name = "EventMgtService", + service = IdentityEventService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetIdentityEventService" + ) + protected void setIdentityEventService(IdentityEventService eventService) { + ConsentExtensionsDataHolder.getInstance().setIdentityEventService(eventService); + } + + protected void unsetIdentityEventService(IdentityEventService eventService) { + + ConsentExtensionsDataHolder.getInstance().setIdentityEventService(null); + } + + @Reference( + name = "realm.service", + service = RealmService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetRealmService" + ) + protected void setRealmService(RealmService realmService) { + + log.debug("Setting the Realm Service"); + ConsentExtensionsDataHolder.getInstance().setRealmService(realmService); + } + + protected void unsetRealmService(RealmService realmService) { + + log.debug("UnSetting the Realm Service"); + ConsentExtensionsDataHolder.getInstance().setRealmService(null); + } } diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/internal/ConsentExtensionsDataHolder.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/internal/ConsentExtensionsDataHolder.java index 553d2d0d..33cdd88c 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/internal/ConsentExtensionsDataHolder.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/main/java/com/wso2/openbanking/accelerator/consent/extensions/internal/ConsentExtensionsDataHolder.java @@ -27,6 +27,8 @@ import com.wso2.openbanking.accelerator.consent.mgt.service.ConsentCoreService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.event.services.IdentityEventService; +import org.wso2.carbon.user.core.service.RealmService; /** * Contains Data holder class for consent extensions. @@ -37,11 +39,12 @@ public class ConsentExtensionsDataHolder { private static volatile ConsentExtensionsDataHolder instance; private OpenBankingConfigurationService openBankingConfigurationService; private ConsentCoreService consentCoreService; - + private RealmService realmService; private ConsentStepsBuilder consentStepsBuilder; private ConsentAdminBuilder consentAdminBuilder; private ConsentManageBuilder consentManageBuilder; private ConsentValidateBuilder consentValidateBuilder; + private IdentityEventService identityEventService; // Prevent instantiation private ConsentExtensionsDataHolder() {} @@ -132,4 +135,20 @@ public ConsentCoreService getConsentCoreService() { public void setConsentCoreService(ConsentCoreService consentCoreService) { this.consentCoreService = consentCoreService; } + + public IdentityEventService getIdentityEventService() { + return identityEventService; + } + + public void setIdentityEventService(IdentityEventService identityEventService) { + this.identityEventService = identityEventService; + } + + public RealmService getRealmService() { + return realmService; + } + + public void setRealmService(RealmService realmService) { + this.realmService = realmService; + } } diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/vrp/persistence/flow/ConsentPersistStepTests.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/vrp/persistence/flow/ConsentPersistStepTests.java index db75703c..299d34d4 100644 --- a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/vrp/persistence/flow/ConsentPersistStepTests.java +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/authorize/vrp/persistence/flow/ConsentPersistStepTests.java @@ -24,6 +24,7 @@ import com.wso2.openbanking.accelerator.consent.extensions.authorize.model.ConsentData; import com.wso2.openbanking.accelerator.consent.extensions.authorize.model.ConsentPersistData; import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentException; +import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentExtensionUtils; import com.wso2.openbanking.accelerator.consent.extensions.common.ConsentServiceUtil; import com.wso2.openbanking.accelerator.consent.extensions.utils.ConsentAuthorizeTestConstants; import com.wso2.openbanking.accelerator.consent.extensions.utils.ConsentExtensionTestUtils; @@ -52,7 +53,7 @@ /** * Test class for Consent Persistence. */ -@PrepareForTest({OpenBankingConfigParser.class, ConsentServiceUtil.class}) +@PrepareForTest({OpenBankingConfigParser.class, ConsentServiceUtil.class, ConsentExtensionUtils.class}) @PowerMockIgnore({"com.wso2.openbanking.accelerator.consent.extensions.common.*", "net.minidev.*", "jdk.internal.reflect.*"}) public class ConsentPersistStepTests { @@ -109,6 +110,9 @@ public void initMethod() { PowerMockito.mockStatic(ConsentServiceUtil.class); when(ConsentServiceUtil.getConsentService()).thenReturn(consentCoreServiceMock); + + PowerMockito.mockStatic(ConsentExtensionUtils.class); + when(ConsentExtensionUtils.isCibaWebAuthLinkFlow(Mockito.any())).thenReturn(false); } @Test(priority = 1, expectedExceptions = ConsentException.class) diff --git a/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/weblink/CIBAWebLinkAuthenticatorTests.java b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/weblink/CIBAWebLinkAuthenticatorTests.java new file mode 100644 index 00000000..d0cf7d6a --- /dev/null +++ b/open-banking-accelerator/components/consent-management/com.wso2.openbanking.accelerator.consent.extensions/src/test/java/com/wso2/openbanking/accelerator/consent/extensions/ciba/weblink/CIBAWebLinkAuthenticatorTests.java @@ -0,0 +1,348 @@ +/** + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. 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 com.wso2.openbanking.accelerator.consent.extensions.ciba.weblink; + +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import com.wso2.openbanking.accelerator.common.config.OpenBankingConfigParser; +import com.wso2.openbanking.accelerator.common.constant.OpenBankingConstants; +import com.wso2.openbanking.accelerator.common.exception.ConsentManagementException; +import com.wso2.openbanking.accelerator.common.util.CarbonUtils; +import com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink.CIBAWebLinkAuthenticator; +import com.wso2.openbanking.accelerator.consent.extensions.ciba.authenticator.weblink.CIBAWebLinkAuthenticatorConstants; +import com.wso2.openbanking.accelerator.consent.extensions.internal.ConsentExtensionsDataHolder; +import com.wso2.openbanking.accelerator.consent.mgt.dao.models.AuthorizationResource; +import com.wso2.openbanking.accelerator.consent.mgt.service.impl.ConsentCoreServiceImpl; +import com.wso2.openbanking.accelerator.identity.internal.IdentityExtensionsDataHolder; +import net.minidev.json.JSONObject; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.testng.PowerMockTestCase; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.wso2.carbon.extension.identity.helper.FederatedAuthenticatorUtil; +import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; +import org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; +import org.wso2.carbon.identity.event.services.IdentityEventService; +import org.wso2.carbon.user.api.UserStoreException; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.UUID; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +@PowerMockIgnore("jdk.internal.reflect.*") +@PrepareForTest({ConsentExtensionsDataHolder.class, IdentityExtensionsDataHolder.class, AuthenticatedUser.class, + FederatedAuthenticatorUtil.class, SignedJWT.class, OpenBankingConfigParser.class, CarbonUtils.class, + IdentityEventService.class}) +public class CIBAWebLinkAuthenticatorTests extends PowerMockTestCase { + + private ConsentCoreServiceImpl consentCoreServiceImpl; + private OpenBankingConfigParser openBankingConfigParser; + + private static ByteArrayOutputStream outContent; + private static PrintStream printStream; + + @BeforeClass + public void initTest() { + + consentCoreServiceImpl = Mockito.mock(ConsentCoreServiceImpl.class); + openBankingConfigParser = Mockito.mock(OpenBankingConfigParser.class); + } + + @BeforeClass + public void beforeTests() { + + outContent = new ByteArrayOutputStream(); + printStream = new PrintStream(outContent); + System.setOut(printStream); + } + + @BeforeMethod + private void mockStaticClasses() { + + PowerMockito.mockStatic(ConsentExtensionsDataHolder.class); + PowerMockito.mockStatic(CarbonUtils.class); + ConsentExtensionsDataHolder mock = PowerMockito.mock(ConsentExtensionsDataHolder.class); + PowerMockito.when(ConsentExtensionsDataHolder.getInstance()).thenReturn(mock); + PowerMockito.when(ConsentExtensionsDataHolder.getInstance().getConsentCoreService()) + .thenReturn(consentCoreServiceImpl); + IdentityEventService identityEventService = PowerMockito.mock(IdentityEventService.class); + PowerMockito.when(ConsentExtensionsDataHolder.getInstance().getIdentityEventService()) + .thenReturn(identityEventService); + PowerMockito.mockStatic(OpenBankingConfigParser.class); + PowerMockito.when(OpenBankingConfigParser.getInstance()).thenReturn(openBankingConfigParser); + } + + @Test(expectedExceptions = AuthenticationFailedException.class, + expectedExceptionsMessageRegExp = "User does not exist in the User store") + public void testInvalidLoginHintCase() throws AuthenticationFailedException, UserStoreException { + + CIBAWebLinkAuthenticatorMock mockAuthenticator = spy(new CIBAWebLinkAuthenticatorMock()); + + HttpServletRequest httpServletRequest = mock(HttpServletRequest.class); + HttpServletResponse httpServletResponse = mock(HttpServletResponse.class); + AuthenticationContext authenticationContext = mock(AuthenticationContext.class); + PowerMockito.when(httpServletRequest.getParameter(Mockito.anyString())).thenReturn("invalidUser"); + + PowerMockito.mockStatic(AuthenticatedUser.class); + PowerMockito.mockStatic(FederatedAuthenticatorUtil.class); + + AuthenticatedUser authenticatedUser = new AuthenticatedUser(); + authenticatedUser.setUserName("invalidUser"); + PowerMockito.when(AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier(Mockito.anyString())) + .thenReturn(authenticatedUser); + PowerMockito.when(FederatedAuthenticatorUtil.isUserExistInUserStore(Mockito.anyString())) + .thenReturn(false); + mockAuthenticator.initiateAuthenticationRequest(httpServletRequest, httpServletResponse, authenticationContext); + } + + @Test(expectedExceptions = AuthenticationFailedException.class, + expectedExceptionsMessageRegExp = "Cannot find the user in User store") + public void testUserStoreExceptionCase() throws AuthenticationFailedException, UserStoreException { + + CIBAWebLinkAuthenticatorMock mockAuthenticator = spy(new CIBAWebLinkAuthenticatorMock()); + + HttpServletRequest httpServletRequest = mock(HttpServletRequest.class); + HttpServletResponse httpServletResponse = mock(HttpServletResponse.class); + AuthenticationContext authenticationContext = mock(AuthenticationContext.class); + PowerMockito.when(httpServletRequest.getParameter(Mockito.anyString())).thenReturn("invalidUser"); + + PowerMockito.mockStatic(AuthenticatedUser.class); + PowerMockito.mockStatic(FederatedAuthenticatorUtil.class); + + AuthenticatedUser authenticatedUser = new AuthenticatedUser(); + authenticatedUser.setUserName("invalidUser"); + PowerMockito.when(AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier(Mockito.anyString())) + .thenReturn(authenticatedUser); + PowerMockito.when(FederatedAuthenticatorUtil.isUserExistInUserStore(Mockito.anyString())) + .thenThrow(new UserStoreException()); + mockAuthenticator.initiateAuthenticationRequest(httpServletRequest, httpServletResponse, authenticationContext); + } + + @Test(expectedExceptions = AuthenticationFailedException.class, + expectedExceptionsMessageRegExp = "Could not extract request object from the request.") + public void testAuthResourcesCreationErrorCase() throws AuthenticationFailedException, UserStoreException { + CIBAWebLinkAuthenticatorMock mockAuthenticator = spy(new CIBAWebLinkAuthenticatorMock()); + + HttpServletRequest httpServletRequest = mock(HttpServletRequest.class); + HttpServletResponse httpServletResponse = mock(HttpServletResponse.class); + AuthenticationContext authenticationContext = new AuthenticationContext(); + PowerMockito.when(httpServletRequest.getParameter(Mockito.anyString())).thenReturn("validUser"); + + PowerMockito.mockStatic(AuthenticatedUser.class); + PowerMockito.mockStatic(FederatedAuthenticatorUtil.class); + PowerMockito.mockStatic(SignedJWT.class); + + AuthenticatedUser authenticatedUser = new AuthenticatedUser(); + authenticatedUser.setUserName("validUser"); + PowerMockito.when(AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier(Mockito.anyString())) + .thenReturn(authenticatedUser); + PowerMockito.when(FederatedAuthenticatorUtil.isUserExistInUserStore(Mockito.anyString())) + .thenReturn(true); + authenticationContext.setQueryParams("&object=test"); + mockAuthenticator.initiateAuthenticationRequest(httpServletRequest, httpServletResponse, authenticationContext); + } + + @Test(expectedExceptions = AuthenticationFailedException.class, + expectedExceptionsMessageRegExp = "No matching authorisation resources found for the given consent.") + public void testAuthResourcesCreationMultiUserErrorCase() throws AuthenticationFailedException, UserStoreException, + ParseException, ConsentManagementException { + CIBAWebLinkAuthenticatorMock mockAuthenticator = spy(new CIBAWebLinkAuthenticatorMock()); + + HttpServletRequest httpServletRequest = mock(HttpServletRequest.class); + HttpServletResponse httpServletResponse = mock(HttpServletResponse.class); + AuthenticationContext authenticationContext = new AuthenticationContext(); + PowerMockito.when(httpServletRequest.getParameter(Mockito.anyString())).thenReturn("user1 , user2"); + + PowerMockito.mockStatic(AuthenticatedUser.class); + PowerMockito.mockStatic(FederatedAuthenticatorUtil.class); + PowerMockito.mockStatic(SignedJWT.class); + + AuthenticatedUser authenticatedUser = new AuthenticatedUser(); + authenticatedUser.setUserName("user1"); + AuthenticatedUser authenticatedUser2 = new AuthenticatedUser(); + authenticatedUser2.setUserName("user2"); + PowerMockito.when(AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier("user1")) + .thenReturn(authenticatedUser); + PowerMockito.when(AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier("user2")) + .thenReturn(authenticatedUser2); + PowerMockito.when(FederatedAuthenticatorUtil.isUserExistInUserStore(Mockito.anyString())) + .thenReturn(true); + authenticationContext.setQueryParams("&request_object=test"); + + SignedJWT signedJWT = mock(SignedJWT.class); + JSONObject claim = new JSONObject(); + JSONObject userInfoClaim = new JSONObject(); + JSONObject consentIdObject = new JSONObject(); + + consentIdObject.put(CIBAWebLinkAuthenticatorConstants.VALUE, "abc"); + userInfoClaim.put(CIBAWebLinkAuthenticatorConstants.OPEN_BANKING_INTENT_ID, consentIdObject); + claim.put(CIBAWebLinkAuthenticatorConstants.USER_INFO, userInfoClaim); + JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder() + .claim(CIBAWebLinkAuthenticatorConstants.CLAIMS, claim).build(); + PowerMockito.when(SignedJWT.parse(Mockito.anyString())).thenReturn(signedJWT); + PowerMockito.when(signedJWT.getJWTClaimsSet()).thenReturn(jwtClaimsSet); + + ArrayList authorizationResources = new ArrayList<>(); + AuthorizationResource authorizationResource = new AuthorizationResource(); + authorizationResource.setAuthorizationStatus(OpenBankingConstants.CREATED_AUTHORISATION_RESOURCE_STATE); + authorizationResource.setAuthorizationID(String.valueOf(UUID.randomUUID())); + authorizationResource.setUserID("user1@carbon.super"); + + AuthorizationResource authorizationResource2 = new AuthorizationResource(); + authorizationResource2.setAuthorizationStatus(OpenBankingConstants.AUTHORISED_STATUS); + authorizationResource2.setAuthorizationID(String.valueOf(UUID.randomUUID())); + authorizationResource2.setUserID("user3@carbon.super"); + + authorizationResources.add(authorizationResource); + authorizationResources.add(authorizationResource2); + PowerMockito.when(consentCoreServiceImpl.searchAuthorizations(Mockito.anyString())) + .thenReturn(authorizationResources); + mockAuthenticator.initiateAuthenticationRequest(httpServletRequest, httpServletResponse, authenticationContext); + } + + @Test(expectedExceptions = AuthenticationFailedException.class, + expectedExceptionsMessageRegExp = "Authorisation resources partially exists for the given consent.") + public void testAuthResourcesCreationMultiUserErrorCase2() throws AuthenticationFailedException, UserStoreException, + ParseException, ConsentManagementException { + CIBAWebLinkAuthenticatorMock mockAuthenticator = spy(new CIBAWebLinkAuthenticatorMock()); + + HttpServletRequest httpServletRequest = mock(HttpServletRequest.class); + HttpServletResponse httpServletResponse = mock(HttpServletResponse.class); + AuthenticationContext authenticationContext = new AuthenticationContext(); + PowerMockito.when(httpServletRequest.getParameter(Mockito.anyString())).thenReturn("user1 , user2"); + + PowerMockito.mockStatic(AuthenticatedUser.class); + PowerMockito.mockStatic(FederatedAuthenticatorUtil.class); + PowerMockito.mockStatic(SignedJWT.class); + + AuthenticatedUser authenticatedUser = new AuthenticatedUser(); + authenticatedUser.setUserName("user1"); + AuthenticatedUser authenticatedUser2 = new AuthenticatedUser(); + authenticatedUser2.setUserName("user2"); + PowerMockito.when(AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier("user1")) + .thenReturn(authenticatedUser); + PowerMockito.when(AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier("user2")) + .thenReturn(authenticatedUser2); + PowerMockito.when(FederatedAuthenticatorUtil.isUserExistInUserStore(Mockito.anyString())) + .thenReturn(true); + authenticationContext.setQueryParams("&request_object=test"); + + SignedJWT signedJWT = mock(SignedJWT.class); + JSONObject claim = new JSONObject(); + JSONObject userInfoClaim = new JSONObject(); + JSONObject consentIdObject = new JSONObject(); + + consentIdObject.put(CIBAWebLinkAuthenticatorConstants.VALUE, "abc"); + userInfoClaim.put(CIBAWebLinkAuthenticatorConstants.OPEN_BANKING_INTENT_ID, consentIdObject); + claim.put(CIBAWebLinkAuthenticatorConstants.USER_INFO, userInfoClaim); + JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder() + .claim(CIBAWebLinkAuthenticatorConstants.CLAIMS, claim).build(); + PowerMockito.when(SignedJWT.parse(Mockito.anyString())).thenReturn(signedJWT); + PowerMockito.when(signedJWT.getJWTClaimsSet()).thenReturn(jwtClaimsSet); + + ArrayList authorizationResources = new ArrayList<>(); + AuthorizationResource authorizationResource = new AuthorizationResource(); + authorizationResource.setAuthorizationStatus(OpenBankingConstants.CREATED_AUTHORISATION_RESOURCE_STATE); + authorizationResource.setAuthorizationID(String.valueOf(UUID.randomUUID())); + authorizationResource.setUserID("user1@carbon.super"); + + AuthorizationResource authorizationResource2 = new AuthorizationResource(); + authorizationResource2.setAuthorizationStatus(OpenBankingConstants.AUTHORISED_STATUS); + authorizationResource2.setAuthorizationID(String.valueOf(UUID.randomUUID())); + authorizationResource2.setUserID("user2@carbon.super"); + + authorizationResources.add(authorizationResource); + authorizationResources.add(authorizationResource2); + authorizationResources.add(authorizationResource2); + PowerMockito.when(consentCoreServiceImpl.searchAuthorizations(Mockito.anyString())) + .thenReturn(authorizationResources); + mockAuthenticator.initiateAuthenticationRequest(httpServletRequest, httpServletResponse, authenticationContext); + } + + @Test() + public void testAuthResourcesCreationSuccessCase() throws AuthenticationFailedException, UserStoreException, + ParseException, ConsentManagementException { + CIBAWebLinkAuthenticatorMock mockAuthenticator = spy(new CIBAWebLinkAuthenticatorMock()); + + HttpServletRequest httpServletRequest = mock(HttpServletRequest.class); + HttpServletResponse httpServletResponse = mock(HttpServletResponse.class); + AuthenticationContext authenticationContext = new AuthenticationContext(); + PowerMockito.when(httpServletRequest.getParameter(Mockito.anyString())).thenReturn("validUser"); + + PowerMockito.mockStatic(AuthenticatedUser.class); + PowerMockito.mockStatic(FederatedAuthenticatorUtil.class); + PowerMockito.mockStatic(SignedJWT.class); + + AuthenticatedUser authenticatedUser = new AuthenticatedUser(); + authenticatedUser.setUserName("validUser"); + PowerMockito.when(AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier(Mockito.anyString())) + .thenReturn(authenticatedUser); + PowerMockito.when(FederatedAuthenticatorUtil.isUserExistInUserStore(Mockito.anyString())) + .thenReturn(true); + authenticationContext.setQueryParams("&request_object=test"); + + SignedJWT signedJWT = mock(SignedJWT.class); + JSONObject claim = new JSONObject(); + JSONObject userInfoClaim = new JSONObject(); + JSONObject consentIdObject = new JSONObject(); + + consentIdObject.put(CIBAWebLinkAuthenticatorConstants.VALUE, "abc"); + userInfoClaim.put(CIBAWebLinkAuthenticatorConstants.OPEN_BANKING_INTENT_ID, consentIdObject); + claim.put(CIBAWebLinkAuthenticatorConstants.USER_INFO, userInfoClaim); + JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder() + .claim(CIBAWebLinkAuthenticatorConstants.CLAIMS, claim).build(); + PowerMockito.when(SignedJWT.parse(Mockito.anyString())).thenReturn(signedJWT); + PowerMockito.when(signedJWT.getJWTClaimsSet()).thenReturn(jwtClaimsSet); + + ArrayList authorizationResources = new ArrayList<>(); + AuthorizationResource authorizationResource = new AuthorizationResource(); + authorizationResource.setAuthorizationStatus(OpenBankingConstants.CREATED_AUTHORISATION_RESOURCE_STATE); + authorizationResource.setAuthorizationID(String.valueOf(UUID.randomUUID())); + authorizationResources.add(authorizationResource); + PowerMockito.when(consentCoreServiceImpl.searchAuthorizations(Mockito.anyString())) + .thenReturn(authorizationResources); + PowerMockito.when(openBankingConfigParser.getCibaWebLinkAllowedParams()).thenReturn(new ArrayList<>()); + PowerMockito.when(CarbonUtils.getCarbonServerUrl()).thenReturn(""); + mockAuthenticator.initiateAuthenticationRequest(httpServletRequest, httpServletResponse, authenticationContext); + } + + static class CIBAWebLinkAuthenticatorMock extends CIBAWebLinkAuthenticator { + + @Override + protected void initiateAuthenticationRequest(HttpServletRequest request, HttpServletResponse response, + AuthenticationContext context) + throws AuthenticationFailedException { + super.initiateAuthenticationRequest(request, response, context); + } + } + +} diff --git a/pom.xml b/pom.xml index d9b458dc..22682387 100644 --- a/pom.xml +++ b/pom.xml @@ -392,6 +392,25 @@ org.wso2.carbon.identity.application.authentication.framework ${carbon.identity.version} + + org.wso2.carbon.identity.event.handler.notification + org.wso2.carbon.identity.event.handler.notification + ${identity.event.handler.notification.version} + + + org.ops4j.pax.logging + pax-logging-log4j2 + + + org.wso2.org.ops4j.pax.logging + pax-logging-api + + + org.ops4j.pax.logging + pax-logging-api + + + org.wso2.carbon.identity.application.auth.basic org.wso2.carbon.identity.application.authenticator.basicauth @@ -824,7 +843,8 @@ 1.0.0.wso2v3 1.12.0 1.2.0.wso2v1 - + 1.7.7 + [1.7.0, 2.0.0) [1.2.0, 2.0.0)