diff --git a/components/org.wso2.carbon.identity.oauth/pom.xml b/components/org.wso2.carbon.identity.oauth/pom.xml index 26158431a6..cbbad0e5c8 100644 --- a/components/org.wso2.carbon.identity.oauth/pom.xml +++ b/components/org.wso2.carbon.identity.oauth/pom.xml @@ -111,6 +111,10 @@ org.wso2.carbon.identity.framework org.wso2.carbon.identity.action.execution + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.rule.evaluation + org.wso2.carbon.identity.framework org.wso2.carbon.identity.event @@ -431,6 +435,10 @@ org.wso2.carbon.identity.application.common.*;version="${carbon.identity.framework.imp.pkg.version.range}", org.wso2.carbon.identity.application.authentication.framework.*;version="${carbon.identity.framework.imp.pkg.version.range}", org.wso2.carbon.identity.user.store.configuration.*;version="${carbon.identity.framework.imp.pkg.version.range}", + org.wso2.carbon.identity.rule.evaluation.model; version="${carbon.identity.framework.imp.pkg.version.range}", + org.wso2.carbon.identity.rule.evaluation.exception; version="${carbon.identity.framework.imp.pkg.version.range}", + org.wso2.carbon.identity.rule.evaluation.provider; version="${carbon.identity.framework.imp.pkg.version.range}", + org.wso2.carbon.identity.oauth.common.token.bindings.*;version="${identity.inbound.auth.oauth.imp.pkg.version.range}", org.wso2.carbon.identity.oauth.common.*;version="${identity.inbound.auth.oauth.imp.pkg.version.range}", org.wso2.carbon.identity.saml.common.util.*; version="${saml.common.util.version.range}", diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/PreIssueAccessTokenRequestBuilder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/execution/PreIssueAccessTokenRequestBuilder.java similarity index 99% rename from components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/PreIssueAccessTokenRequestBuilder.java rename to components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/execution/PreIssueAccessTokenRequestBuilder.java index 9f88024419..0cc016f424 100755 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/PreIssueAccessTokenRequestBuilder.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/execution/PreIssueAccessTokenRequestBuilder.java @@ -16,7 +16,7 @@ * under the License. */ -package org.wso2.carbon.identity.oauth.action; +package org.wso2.carbon.identity.oauth.action.execution; import com.nimbusds.jwt.JWTClaimsSet; import org.apache.commons.logging.Log; diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/PreIssueAccessTokenResponseProcessor.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/execution/PreIssueAccessTokenResponseProcessor.java similarity index 99% rename from components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/PreIssueAccessTokenResponseProcessor.java rename to components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/execution/PreIssueAccessTokenResponseProcessor.java index b4bd8d1b67..1657577e7c 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/PreIssueAccessTokenResponseProcessor.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/execution/PreIssueAccessTokenResponseProcessor.java @@ -16,7 +16,7 @@ * under the License. */ -package org.wso2.carbon.identity.oauth.action; +package org.wso2.carbon.identity.oauth.action.execution; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/rule/PreIssueAccessTokenRuleEvaluationDataProvider.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/rule/PreIssueAccessTokenRuleEvaluationDataProvider.java new file mode 100644 index 0000000000..e7d23374a5 --- /dev/null +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/action/rule/PreIssueAccessTokenRuleEvaluationDataProvider.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://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 org.wso2.carbon.identity.oauth.action.rule; + +import org.wso2.carbon.identity.application.common.model.ServiceProvider; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenReqDTO; +import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext; +import org.wso2.carbon.identity.oauth2.util.OAuth2Util; +import org.wso2.carbon.identity.rule.evaluation.exception.RuleEvaluationDataProviderException; +import org.wso2.carbon.identity.rule.evaluation.model.Field; +import org.wso2.carbon.identity.rule.evaluation.model.FieldValue; +import org.wso2.carbon.identity.rule.evaluation.model.FlowContext; +import org.wso2.carbon.identity.rule.evaluation.model.FlowType; +import org.wso2.carbon.identity.rule.evaluation.model.RuleEvaluationContext; +import org.wso2.carbon.identity.rule.evaluation.model.ValueType; +import org.wso2.carbon.identity.rule.evaluation.provider.RuleEvaluationDataProvider; + +import java.util.ArrayList; +import java.util.List; + +/** + * Rule evaluation data provider for pre issue access token flow. + * This class provides the data required for rule evaluation in pre issue access token flow. + */ +public class PreIssueAccessTokenRuleEvaluationDataProvider implements RuleEvaluationDataProvider { + + private enum RuleField { + APPLICATION("application"), + GRANT_TYPE("grantType"); + + final String fieldName; + + RuleField(String fieldName) { + + this.fieldName = fieldName; + } + + public String getFieldName() { + + return fieldName; + } + + public static RuleField valueOfFieldName(String fieldName) throws RuleEvaluationDataProviderException { + + for (RuleField ruleField : RuleField.values()) { + if (ruleField.getFieldName().equals(fieldName)) { + return ruleField; + } + } + + throw new RuleEvaluationDataProviderException("Unsupported field: " + fieldName); + } + } + + @Override + public FlowType getSupportedFlowType() { + + return FlowType.PRE_ISSUE_ACCESS_TOKEN; + } + + @Override + public List getEvaluationData(RuleEvaluationContext ruleEvaluationContext, + FlowContext flowContext, String tenantDomain) + throws RuleEvaluationDataProviderException { + + OAuthTokenReqMessageContext tokenMessageContext = + (OAuthTokenReqMessageContext) flowContext.getContextData().get("tokenMessageContext"); + + OAuth2AccessTokenReqDTO tokenReqDTO = tokenMessageContext.getOauth2AccessTokenReqDTO(); + List fieldValueList = new ArrayList<>(); + + for (Field field : ruleEvaluationContext.getFields()) { + switch (RuleField.valueOfFieldName(field.getName())) { + case APPLICATION: + addApplicationFieldValue(fieldValueList, field, tokenReqDTO); + break; + case GRANT_TYPE: + fieldValueList.add(new FieldValue(field.getName(), tokenReqDTO.getGrantType(), ValueType.STRING)); + break; + default: + throw new RuleEvaluationDataProviderException("Unsupported field: " + field.getName()); + } + } + + return fieldValueList; + } + + private void addApplicationFieldValue(List fieldValueList, Field field, + OAuth2AccessTokenReqDTO tokenReqDTO) + throws RuleEvaluationDataProviderException { + + try { + ServiceProvider application = + OAuth2Util.getServiceProvider(tokenReqDTO.getClientId(), tokenReqDTO.getTenantDomain()); + if (application != null) { + fieldValueList.add( + new FieldValue(field.getName(), application.getApplicationResourceId(), ValueType.REFERENCE)); + } + } catch (IdentityOAuth2Exception e) { + throw new RuleEvaluationDataProviderException("Error retrieving service provider", e); + } + } +} diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthServiceComponent.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthServiceComponent.java index 3d24ba3dc9..c56215ab76 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthServiceComponent.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/internal/OAuthServiceComponent.java @@ -39,8 +39,9 @@ import org.wso2.carbon.identity.event.handler.AbstractEventHandler; import org.wso2.carbon.identity.oauth.OAuthAdminServiceImpl; import org.wso2.carbon.identity.oauth.OauthInboundAuthConfigHandler; -import org.wso2.carbon.identity.oauth.action.PreIssueAccessTokenRequestBuilder; -import org.wso2.carbon.identity.oauth.action.PreIssueAccessTokenResponseProcessor; +import org.wso2.carbon.identity.oauth.action.execution.PreIssueAccessTokenRequestBuilder; +import org.wso2.carbon.identity.oauth.action.execution.PreIssueAccessTokenResponseProcessor; +import org.wso2.carbon.identity.oauth.action.rule.PreIssueAccessTokenRuleEvaluationDataProvider; import org.wso2.carbon.identity.oauth.cache.OAuthCache; import org.wso2.carbon.identity.oauth.common.OAuthConstants; import org.wso2.carbon.identity.oauth.common.token.bindings.TokenBinderInfo; @@ -58,6 +59,7 @@ import org.wso2.carbon.identity.organization.management.service.OrganizationManager; import org.wso2.carbon.identity.organization.management.service.OrganizationUserResidentResolverService; import org.wso2.carbon.identity.role.mgt.core.RoleManagementService; +import org.wso2.carbon.identity.rule.evaluation.provider.RuleEvaluationDataProvider; import org.wso2.carbon.idp.mgt.IdpManager; import org.wso2.carbon.user.core.listener.UserOperationEventListener; import org.wso2.carbon.user.core.service.RealmService; @@ -118,6 +120,7 @@ protected void activate(ComponentContext context) { authProtocolApplicationService, null); registerActionRequestBuilderAndResponseProcessor(context); + registerRuleEvaluationDataProvider(context); // Note : DO NOT add any activation related code below this point, // to make sure the server doesn't start up if any activation failures occur @@ -140,6 +143,13 @@ private void registerActionRequestBuilderAndResponseProcessor(ComponentContext c null); } + private void registerRuleEvaluationDataProvider(ComponentContext context) { + + context.getBundleContext() + .registerService(RuleEvaluationDataProvider.class, new PreIssueAccessTokenRuleEvaluationDataProvider(), + null); + } + protected void deactivate(ComponentContext context) { if (serviceRegistration != null) { diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/action/PreIssueAccessTokenRequestBuilderTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/action/execution/PreIssueAccessTokenRequestBuilderTest.java similarity index 99% rename from components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/action/PreIssueAccessTokenRequestBuilderTest.java rename to components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/action/execution/PreIssueAccessTokenRequestBuilderTest.java index ff9f8bfc0b..fe8cf0a16c 100644 --- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/action/PreIssueAccessTokenRequestBuilderTest.java +++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/action/execution/PreIssueAccessTokenRequestBuilderTest.java @@ -16,7 +16,7 @@ * under the License. */ -package org.wso2.carbon.identity.oauth.action; +package org.wso2.carbon.identity.oauth.action.execution; import org.apache.commons.codec.binary.Base64; import org.mockito.MockedStatic; diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/action/rule/PreIssueAccessTokenRuleEvaluationDataProviderTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/action/rule/PreIssueAccessTokenRuleEvaluationDataProviderTest.java new file mode 100644 index 0000000000..8f3fb2b767 --- /dev/null +++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth/action/rule/PreIssueAccessTokenRuleEvaluationDataProviderTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://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 org.wso2.carbon.identity.oauth.action.rule; + +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.MockitoAnnotations; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.application.common.model.ServiceProvider; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenReqDTO; +import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext; +import org.wso2.carbon.identity.oauth2.util.OAuth2Util; +import org.wso2.carbon.identity.rule.evaluation.exception.RuleEvaluationDataProviderException; +import org.wso2.carbon.identity.rule.evaluation.model.Field; +import org.wso2.carbon.identity.rule.evaluation.model.FieldValue; +import org.wso2.carbon.identity.rule.evaluation.model.FlowContext; +import org.wso2.carbon.identity.rule.evaluation.model.FlowType; +import org.wso2.carbon.identity.rule.evaluation.model.RuleEvaluationContext; +import org.wso2.carbon.identity.rule.evaluation.model.ValueType; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; + +public class PreIssueAccessTokenRuleEvaluationDataProviderTest { + + @InjectMocks + private PreIssueAccessTokenRuleEvaluationDataProvider provider; + @Mock + private OAuthTokenReqMessageContext tokenMessageContext; + @Mock + private OAuth2AccessTokenReqDTO tokenReqDTO; + @Mock + private FlowContext flowContext; + @Mock + private RuleEvaluationContext ruleEvaluationContext; + @Mock + private ServiceProvider serviceProvider; + private MockedStatic oAuth2UtilMockedStatic; + + @BeforeMethod + public void setUp() { + + MockitoAnnotations.openMocks(this); + when(flowContext.getContextData()).thenReturn( + Collections.singletonMap("tokenMessageContext", tokenMessageContext)); + when(tokenMessageContext.getOauth2AccessTokenReqDTO()).thenReturn(tokenReqDTO); + + oAuth2UtilMockedStatic = mockStatic(OAuth2Util.class); + } + + @AfterMethod + public void tearDown() { + + oAuth2UtilMockedStatic.close(); + } + + @Test + public void testGetSupportedFlowType() { + + assertEquals(provider.getSupportedFlowType(), FlowType.PRE_ISSUE_ACCESS_TOKEN); + } + + @Test + public void testGetEvaluationDataWithValidFields() throws Exception { + + Field applicationField = new Field("application", ValueType.REFERENCE); + Field grantTypeField = new Field("grantType", ValueType.STRING); + when(ruleEvaluationContext.getFields()).thenReturn(Arrays.asList(applicationField, grantTypeField)); + + when(tokenReqDTO.getClientId()).thenReturn("clientId"); + when(tokenReqDTO.getTenantDomain()).thenReturn("tenantDomain"); + when(tokenReqDTO.getGrantType()).thenReturn("authorization_code"); + + when(OAuth2Util.getServiceProvider(anyString(), anyString())).thenReturn(serviceProvider); + when(serviceProvider.getApplicationResourceId()).thenReturn("testapp"); + + List fieldValues = provider.getEvaluationData(ruleEvaluationContext, flowContext, null); + + assertEquals(fieldValues.size(), 2); + assertEquals(fieldValues.get(0).getName(), "application"); + assertEquals(fieldValues.get(0).getValue(), "testapp"); + assertEquals(fieldValues.get(1).getName(), "grantType"); + assertEquals(fieldValues.get(1).getValue(), "authorization_code"); + } + + @Test(expectedExceptions = RuleEvaluationDataProviderException.class, expectedExceptionsMessageRegExp = + "Unsupported field: unsupported") + public void testGetEvaluationDataWithUnsupportedField() throws Exception { + + Field unsupportedField = new Field("unsupported", ValueType.STRING); + when(ruleEvaluationContext.getFields()).thenReturn(Collections.singletonList(unsupportedField)); + + provider.getEvaluationData(ruleEvaluationContext, flowContext, null); + } + + @Test(expectedExceptions = RuleEvaluationDataProviderException.class) + public void testGetEvaluationDataWhenRetrievingServiceProviderFails() throws Exception { + + Field applicationField = new Field("application", ValueType.REFERENCE); + when(ruleEvaluationContext.getFields()).thenReturn(Collections.singletonList(applicationField)); + + when(tokenReqDTO.getClientId()).thenReturn("clientId"); + when(tokenReqDTO.getTenantDomain()).thenReturn("tenantDomain"); + + when(OAuth2Util.getServiceProvider(anyString(), anyString())).thenThrow(new IdentityOAuth2Exception("Error")); + + provider.getEvaluationData(ruleEvaluationContext, flowContext, "tenantDomain"); + } +} diff --git a/components/org.wso2.carbon.identity.oauth/src/test/resources/testng.xml b/components/org.wso2.carbon.identity.oauth/src/test/resources/testng.xml index f1a5b35f9e..4ea7b033b9 100755 --- a/components/org.wso2.carbon.identity.oauth/src/test/resources/testng.xml +++ b/components/org.wso2.carbon.identity.oauth/src/test/resources/testng.xml @@ -212,6 +212,8 @@ + + diff --git a/pom.xml b/pom.xml index 6951d0e3d5..8a62524cb1 100644 --- a/pom.xml +++ b/pom.xml @@ -277,6 +277,11 @@ org.wso2.carbon.identity.action.execution ${carbon.identity.framework.version} + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.rule.evaluation + ${carbon.identity.framework.version} + @@ -962,7 +967,7 @@ [1.0.1, 2.0.0) - 7.7.90 + 7.7.112 [5.25.234, 8.0.0) [2.0.0, 3.0.0)