From 11e01289c5b70a2a6901463c27573b7d09cbfaf7 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:37:19 +0530 Subject: [PATCH 1/3] add a config to autofill the username with subject attribute while jit provisioning --- .../JITProvisioningPostAuthenticationHandler.java | 4 ++++ .../framework/util/FrameworkUtils.java | 13 +++++++++++++ .../resources/identity.xml | 1 + .../resources/identity.xml.j2 | 1 + ...carbon.identity.core.server.feature.default.json | 1 + 5 files changed, 20 insertions(+) diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandler.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandler.java index e2a48cc7bde9..eaf3334afb22 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandler.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandler.java @@ -694,6 +694,10 @@ private void redirectToAccountCreateUI(ExternalIdPConfig externalIdPConfig, Auth uriBuilder.addParameter(FrameworkConstants.SERVICE_PROVIDER, context.getSequenceConfig() .getApplicationConfig().getApplicationName()); uriBuilder.addParameter(FrameworkConstants.USERNAME, username); + if (!externalIdPConfig.isModifyUserNameAllowed() || (externalIdPConfig.isModifyUserNameAllowed() && + FrameworkUtils.isUsernameFieldAutofillWithSubjectAttr())) { + uriBuilder.addParameter(FrameworkConstants.USERNAME, username); + } uriBuilder.addParameter(FrameworkConstants.SKIP_SIGN_UP_ENABLE_CHECK, String.valueOf(true)); uriBuilder.addParameter(FrameworkConstants.SESSION_DATA_KEY, context.getContextIdentifier()); addMissingClaims(uriBuilder, context); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkUtils.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkUtils.java index 21bac2e66b1f..e346a3c25f52 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkUtils.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkUtils.java @@ -3014,6 +3014,19 @@ public static String getUserNameProvisioningUIUrl() { return userNamePrvisioningUrl; } + /** + * Checks if the username field should be autofilled with the subject attribute + * during Just-In-Time (JIT) provisioning with prompt for username, password, and consent. + * + * @return true if the username field should be autofilled with the + * subject attribute; false otherwise. + */ + public static boolean isUsernameFieldAutofillWithSubjectAttr() { + + return Boolean.parseBoolean( + IdentityUtil.getProperty("JITProvisioning.AutofillUsernameFieldWithSubjectAttribute")); + } + /** * This method determines whether username pattern validation should be skipped for JIT provisioning users based * on the configuration file. diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml index 4578a389f1b3..5022e40d750f 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml @@ -1237,6 +1237,7 @@ /accountrecoveryendpoint/register.do + true /accountrecoveryendpoint/signup.do false false diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml.j2 b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml.j2 index 0f3b9924e124..b76c19e39664 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml.j2 +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml.j2 @@ -2018,6 +2018,7 @@ {{authentication.jit_provisioning.username_provisioning_url}} + {{authentication.jit_provisioning.autofill_username_field_with_subject_attribute}} {{authentication.jit_provisioning.password_provisioning_url}} {{authentication.jit_provisioning.fail_authn_on_provision_failure}} {{authentication.jit_provisioning.skip_username_pattern_validation}} diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/org.wso2.carbon.identity.core.server.feature.default.json b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/org.wso2.carbon.identity.core.server.feature.default.json index 199e02614d84..4d1af3a966b3 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/org.wso2.carbon.identity.core.server.feature.default.json +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/org.wso2.carbon.identity.core.server.feature.default.json @@ -526,6 +526,7 @@ "authentication_policy.check_account_exist": true, "authentication.jit_provisioning.username_provisioning_url": "/accountrecoveryendpoint/register.do", + "authentication.jit_provisioning.autofill_username_field_with_subject_attribute": true, "authentication.jit_provisioning.password_provisioning_url": "/accountrecoveryendpoint/signup.do", "authentication.jit_provisioning.skip_username_pattern_validation": false, "authentication.jit_provisioning.fail_authn_on_provision_failure": false, From 515cf11f2d076e538140faa195fba11ed18653a9 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:23:43 +0530 Subject: [PATCH 2/3] add unit tests --- ...ProvisioningPostAuthenticationHandler.java | 1 - ...isioningPostAuthenticationHandlerTest.java | 94 +++++++++++++++++++ .../framework/util/FrameworkUtilsTest.java | 19 ++++ 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandler.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandler.java index eaf3334afb22..ca60572e759f 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandler.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandler.java @@ -693,7 +693,6 @@ private void redirectToAccountCreateUI(ExternalIdPConfig externalIdPConfig, Auth } uriBuilder.addParameter(FrameworkConstants.SERVICE_PROVIDER, context.getSequenceConfig() .getApplicationConfig().getApplicationName()); - uriBuilder.addParameter(FrameworkConstants.USERNAME, username); if (!externalIdPConfig.isModifyUserNameAllowed() || (externalIdPConfig.isModifyUserNameAllowed() && FrameworkUtils.isUsernameFieldAutofillWithSubjectAttr())) { uriBuilder.addParameter(FrameworkConstants.USERNAME, username); diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandlerTest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandlerTest.java index 7fe64ac03758..649de4b6d58f 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandlerTest.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandlerTest.java @@ -18,6 +18,9 @@ package org.wso2.carbon.identity.application.authentication.framework.handler.request.impl; +import org.apache.axis2.context.ConfigurationContext; +import org.apache.axis2.engine.AxisConfiguration; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; @@ -25,9 +28,11 @@ import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; import org.testng.annotations.Listeners; import org.testng.annotations.Test; import org.wso2.carbon.CarbonConstants; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.application.authentication.framework.AbstractFrameworkTest; import org.wso2.carbon.identity.application.authentication.framework.ApplicationAuthenticator; import org.wso2.carbon.identity.application.authentication.framework.FederatedApplicationAuthenticator; @@ -46,7 +51,9 @@ import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils; import org.wso2.carbon.identity.application.common.model.IdentityProvider; +import org.wso2.carbon.identity.application.common.model.JustInTimeProvisioningConfig; import org.wso2.carbon.identity.application.common.model.ServiceProvider; +import org.wso2.carbon.identity.core.internal.IdentityCoreServiceComponent; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.user.profile.mgt.association.federation.FederatedAssociationManager; import org.wso2.carbon.identity.user.profile.mgt.association.federation.FederatedAssociationManagerImpl; @@ -54,8 +61,11 @@ import org.wso2.carbon.idp.mgt.IdentityProviderManagementException; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.core.util.UserCoreUtil; +import org.wso2.carbon.utils.CarbonUtils; +import org.wso2.carbon.utils.ConfigurationContextService; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; +import java.io.IOException; import java.util.Collections; import java.util.Map; @@ -70,6 +80,8 @@ import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * This is a test class for {@link JITProvisioningPostAuthenticationHandler}. @@ -88,6 +100,8 @@ public class JITProvisioningPostAuthenticationHandlerTest extends AbstractFramew private MockedStatic frameworkUtils; private MockedStatic configurationFacade; + private MockedStatic carbonUtils; + private MockedStatic privilegedCarbonContextMockedStatic; @BeforeClass protected void setupSuite() throws XMLStreamException, IdentityProviderManagementException { @@ -111,12 +125,15 @@ protected void setupSuite() throws XMLStreamException, IdentityProviderManagemen response = mock(HttpServletResponse.class); postJITProvisioningHandler = JITProvisioningPostAuthenticationHandler.getInstance(); sp = getTestServiceProvider("default-sp-1.xml"); + carbonUtils = mockStatic(CarbonUtils.class); + privilegedCarbonContextMockedStatic = mockStatic(PrivilegedCarbonContext.class); } @AfterClass protected void cleanup() { frameworkUtils.close(); configurationFacade.close(); + carbonUtils.close(); } @Test(description = "This test case tests the Post JIT provisioning handling flow without an authenticated user") @@ -175,6 +192,83 @@ public void testHandleWithAuthenticatedUserWithFederatedIdp() throws FrameworkEx } } + @DataProvider(name = "usernameAutoFillDataProvider") + public Object[][] usernameAutoFillDataProvider() { + + return new Object[][] { + {false, true, true}, + {true, false, false}, + {true, true, true} + }; + } + + @Test(description = "This test case verifies that the username attribute auto-fill functionality works correctly" + + " when username and password provisioning are enabled.", dataProvider = "usernameAutoFillDataProvider") + public void testUsernameAutoFillingFunctionality(boolean isUsernameModifiable, boolean isUsernameAutoFillEnabled, + boolean usernameShouldContainInURL) + throws FrameworkException, XMLStreamException, IdentityProviderManagementException, IOException { + + try (MockedStatic frameworkServiceDataHolder = + mockStatic(FrameworkServiceDataHolder.class); + MockedStatic identityTenantUtil = mockStatic(IdentityTenantUtil.class); + MockedStatic identityCoreServiceComponentMockedStatic = mockStatic( + IdentityCoreServiceComponent.class)) { + frameworkServiceDataHolder.when( + FrameworkServiceDataHolder::getInstance).thenReturn(mockFrameworkServiceDataHolder); + AuthenticationContext context = processAndGetAuthenticationContext(sp, true, true); + FederatedAssociationManager federatedAssociationManager = mock(FederatedAssociationManagerImpl.class); + frameworkUtils.when(FrameworkUtils::getFederatedAssociationManager).thenReturn(federatedAssociationManager); + frameworkUtils.when( + FrameworkUtils::getStepBasedSequenceHandler) + .thenReturn(mock(StepBasedSequenceHandler.class)); + frameworkUtils.when(() -> FrameworkUtils.getMissingClaims(any())) + .thenReturn(new String[] {"test-claim", "test-claim1"}); + frameworkUtils.when(FrameworkUtils::isUsernameFieldAutofillWithSubjectAttr) + .thenReturn(isUsernameAutoFillEnabled); + + identityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(anyString())).thenReturn(1); + + carbonUtils.when(() -> CarbonUtils.getTransportProxyPort(any(AxisConfiguration.class), any())) + .thenReturn(101010); + carbonUtils.when(CarbonUtils::getManagementTransport).thenReturn("https"); + + // Need to mock getIdPConfigByName with a null parameter. + ConfigurationFacade mockConfigurationFacade = mock(ConfigurationFacade.class); + configurationFacade.when(ConfigurationFacade::getInstance).thenReturn(mockConfigurationFacade); + IdentityProvider identityProvider = getTestIdentityProvider("default-tp-1.xml"); + JustInTimeProvisioningConfig justInTimeProvisioningConfig = + identityProvider.getJustInTimeProvisioningConfig(); + justInTimeProvisioningConfig.setPromptConsent(true); + justInTimeProvisioningConfig.setModifyUserNameAllowed(isUsernameModifiable); + identityProvider.setJustInTimeProvisioningConfig(justInTimeProvisioningConfig); + ExternalIdPConfig externalIdPConfig = new ExternalIdPConfig(identityProvider); + doReturn(externalIdPConfig).when(mockConfigurationFacade).getIdPConfigByName(eq(null), anyString()); + + ConfigurationContextService configurationContextService = mock(ConfigurationContextService.class); + ConfigurationContext configurationContext = mock(ConfigurationContext.class); + when(configurationContextService.getServerConfigContext()).thenReturn(configurationContext); + identityCoreServiceComponentMockedStatic.when(IdentityCoreServiceComponent::getConfigurationContextService) + .thenReturn(configurationContextService); + + PrivilegedCarbonContext privilegedCarbonContext = mock(PrivilegedCarbonContext.class); + when(privilegedCarbonContext.getTenantDomain()).thenReturn("test-domain"); + privilegedCarbonContextMockedStatic.when(PrivilegedCarbonContext::getThreadLocalCarbonContext) + .thenReturn(privilegedCarbonContext); + + HttpServletResponse mockResponse = mock(HttpServletResponse.class); + postJITProvisioningHandler.handle(request, mockResponse, context); + + ArgumentCaptor uiRedirectionUrl = ArgumentCaptor.forClass(String.class); + verify(mockResponse).sendRedirect(uiRedirectionUrl.capture()); + boolean urlContainsUsernameParam = uiRedirectionUrl.getValue().contains("username=test"); + if (usernameShouldContainInURL) { + Assert.assertTrue(urlContainsUsernameParam); + } else { + Assert.assertFalse(urlContainsUsernameParam); + } + } + } + /** * To get the authentication context and to call the handle method of the PostJitProvisioningHandler. * diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkUtilsTest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkUtilsTest.java index 0998a0ae2dff..2daf35c873b7 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkUtilsTest.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/util/FrameworkUtilsTest.java @@ -85,6 +85,7 @@ import javax.servlet.http.HttpServletResponse; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.never; @@ -92,6 +93,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.REQUEST_PARAM_SP; @@ -904,4 +906,21 @@ public void testGetUserIdClaimURI() throws Exception { assertEquals(result, "http://wso2.org/claims/username"); } } + + @Test(description = "Verify that the username auto-fill configuration is retrieved correctly") + public void testGetUsernameFieldAutofillWithSubjectAttrConfig() { + + try (MockedStatic identityUtilMockedStatic = mockStatic(IdentityUtil.class)) { + identityUtilMockedStatic.when( + () -> IdentityUtil.getProperty( + eq("JITProvisioning.AutofillUsernameFieldWithSubjectAttribute"))) + .thenReturn("true"); + assertTrue(FrameworkUtils.isUsernameFieldAutofillWithSubjectAttr()); + identityUtilMockedStatic.when( + () -> IdentityUtil.getProperty( + eq("JITProvisioning.AutofillUsernameFieldWithSubjectAttribute"))) + .thenReturn("false"); + assertFalse(FrameworkUtils.isUsernameFieldAutofillWithSubjectAttr()); + } + } } From eda1110d3f8aaab84e43249c474beb121068ce7f Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:44:33 +0530 Subject: [PATCH 3/3] close the mock static class --- .../impl/JITProvisioningPostAuthenticationHandlerTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandlerTest.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandlerTest.java index 649de4b6d58f..3beb775b3771 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandlerTest.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/test/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/JITProvisioningPostAuthenticationHandlerTest.java @@ -134,6 +134,7 @@ protected void cleanup() { frameworkUtils.close(); configurationFacade.close(); carbonUtils.close(); + privilegedCarbonContextMockedStatic.close(); } @Test(description = "This test case tests the Post JIT provisioning handling flow without an authenticated user")