diff --git a/.github/workflows/dependency-updater-7.yml b/.github/workflows/dependency-updater-7.yml index d9c4a2164ae..b31f43f79ea 100644 --- a/.github/workflows/dependency-updater-7.yml +++ b/.github/workflows/dependency-updater-7.yml @@ -1,5 +1,5 @@ # This workflow will update dependencies for product-is 7.x.x. -name: Dependency Updater 7.x.x +name: "[Deprecated] Dependency Updater 7.x.x" on: workflow_dispatch: # schedule: diff --git a/.github/workflows/pr-builder-test-JDK11-7.yml b/.github/workflows/pr-builder-test-JDK11-7.yml index ba39fa4afc9..63fa41bc3dc 100644 --- a/.github/workflows/pr-builder-test-JDK11-7.yml +++ b/.github/workflows/pr-builder-test-JDK11-7.yml @@ -1,4 +1,4 @@ -name: pr-builder-test 7.x.x +name: "[Deprecated] pr-builder-test 7.x.x" on: workflow_dispatch: diff --git a/modules/distribution/src/repository/resources/conf/templates/repository/conf/tomcat/catalina-server.xml.j2 b/modules/distribution/src/repository/resources/conf/templates/repository/conf/tomcat/catalina-server.xml.j2 index 499861243b9..e32d0405d2a 100644 --- a/modules/distribution/src/repository/resources/conf/templates/repository/conf/tomcat/catalina-server.xml.j2 +++ b/modules/distribution/src/repository/resources/conf/templates/repository/conf/tomcat/catalina-server.xml.j2 @@ -109,6 +109,11 @@ + + {% if tenant_context.enable_tenant_qualified_urls is sameas false %} + + {% endif %} + diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java index 67352c5ca4e..f956ba51848 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java @@ -489,6 +489,31 @@ public HttpResponse sendLoginPostForCustomUsers(HttpClient client, String sessio return sendPostRequestWithParameters(client, urlParameters, OAuth2Constant.COMMON_AUTH_URL); } + /** + * Send login post request for a tenant with given username and password credentials. + * + * @param client Http client. + * @param sessionDataKey Session data key. + * @param username Username. + * @param password Password. + * @param tenantDomain Tenant domain. + * @return Http response. + * @throws ClientProtocolException ClientProtocolException + * @throws IOException IOException + */ + public HttpResponse sendLoginPostForCustomUsers(HttpClient client, String sessionDataKey, String username, + String password, String tenantDomain) + throws ClientProtocolException, IOException { + + List urlParameters = new ArrayList<>(); + urlParameters.add(new BasicNameValuePair("username", username)); + urlParameters.add(new BasicNameValuePair("password", password)); + urlParameters.add(new BasicNameValuePair("sessionDataKey", sessionDataKey)); + log.info(">>> sendLoginPost:sessionDataKey: " + sessionDataKey); + String url = OAuth2Constant.TENANT_COMMON_AUTH_URL.replace(OAuth2Constant.TENANT_PLACEHOLDER, tenantDomain); + return sendPostRequestWithParameters(client, urlParameters, url); + } + /** * Send approval post request * @@ -531,6 +556,33 @@ public HttpResponse sendApprovalPostWithConsent(HttpClient client, String sessio return sendPostRequestWithParameters(client, urlParameters, OAuth2Constant.APPROVAL_URL); } + /** + * Send approval post request for tenant with consent. + * + * @param client http client. + * @param sessionDataKeyConsent session consent data. + * @param consentClaims claims requiring user consent. + * @param tenantDomain tenant domain. + * @return http response. + * @throws java.io.IOException IOException. + */ + public HttpResponse sendApprovalPostWithConsent(HttpClient client, String sessionDataKeyConsent, + List consentClaims, String tenantDomain) + throws IOException { + + List urlParameters = new ArrayList<>(); + urlParameters.add(new BasicNameValuePair("consent", "approve")); + urlParameters.add(new BasicNameValuePair("scope-approval", "approve")); + urlParameters.add(new BasicNameValuePair("sessionDataKeyConsent", sessionDataKeyConsent)); + + if (consentClaims != null) { + urlParameters.addAll(consentClaims); + } + String url = OAuth2Constant.TENANT_APPROVAL_URL.replace(OAuth2Constant.TENANT_PLACEHOLDER, tenantDomain); + + return sendPostRequestWithParameters(client, urlParameters, url); + } + /** * Send approval post request * diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuthAppsWithSameClientIdTestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuthAppsWithSameClientIdTestCase.java new file mode 100644 index 00000000000..26a3dfe7720 --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuthAppsWithSameClientIdTestCase.java @@ -0,0 +1,738 @@ +/* + * Copyright (c) 2023, 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.identity.integration.test.oauth2; + +import org.apache.commons.lang.StringUtils; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.NameValuePair; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.CookieSpecs; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.config.Lookup; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.cookie.CookieSpecProvider; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.cookie.RFC6265CookieSpecProvider; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.json.JSONException; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import org.wso2.carbon.automation.engine.context.beans.Tenant; +import org.wso2.carbon.automation.engine.context.beans.User; +import org.wso2.carbon.integration.common.utils.exceptions.AutomationUtilException; +import org.wso2.carbon.integration.common.utils.mgt.ServerConfigurationManager; +import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationListItem; +import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationListResponse; +import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationModel; +import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationResponseModel; +import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.InboundProtocols; +import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.OpenIDConnectConfiguration; +import org.wso2.identity.integration.test.rest.api.server.tenant.management.v1.model.Owner; +import org.wso2.identity.integration.test.rest.api.server.tenant.management.v1.model.TenantModel; +import org.wso2.identity.integration.test.rest.api.user.common.model.Email; +import org.wso2.identity.integration.test.rest.api.user.common.model.UserObject; +import org.wso2.identity.integration.test.restclients.OAuth2RestClient; +import org.wso2.identity.integration.test.restclients.RestBaseClient; +import org.wso2.identity.integration.test.restclients.SCIM2RestClient; +import org.wso2.identity.integration.test.restclients.TenantMgtRestClient; +import org.wso2.identity.integration.test.util.Utils; +import org.wso2.identity.integration.test.utils.CommonConstants; +import org.wso2.identity.integration.test.utils.DataExtractUtil; +import org.wso2.identity.integration.test.utils.OAuth2Constant; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This class contains tests for OAuth apps with same client id in multiple tenants. + */ +public class OAuthAppsWithSameClientIdTestCase extends OAuth2ServiceAbstractIntegrationTest { + + public final static String DUMMY_CLIENT_ID = "dummy_client_id"; + public final static String DUMMY_CLIENT_SECRET = "dummy_client_secret"; + private static final String TENANT_1_DOMAIN = "tenant1.com"; + private static final String TENANT_1_ADMIN_USERNAME = "admin@tenant1.com"; + private static final String TENANT_1_ADMIN_PASSWORD = "admin"; + private static final String TENANT_1_ADMIN_TENANT_AWARE_USERNAME = "admin"; + private static final String TENANT_1_USER_USERNAME = "userTenant1"; + private static final String TENANT_1_USER_PASSWORD = "userTenant1"; + private static final String TENANT_1_USER_EMAIL = "userTenant1@wso2.com"; + private static final String TENANT_2_DOMAIN = "tenant2.com"; + private static final String TENANT_2_ADMIN_USERNAME = "admin@tenant2.com"; + private static final String TENANT_2_ADMIN_PASSWORD = "admin"; + private static final String TENANT_2_ADMIN_TENANT_AWARE_USERNAME = "admin"; + private static final String TENANT_2_USER_USERNAME = "userTenant2"; + private static final String TENANT_2_USER_PASSWORD = "userTenant2"; + private static final String TENANT_2_USER_EMAIL = "userTenant2@wso2.com"; + public final static String TENANT_1_AUTHORIZE_URL = "https://localhost:9853/t/tenant1.com/oauth2/authorize"; + + private ServerConfigurationManager serverConfigurationManager; + private TenantMgtRestClient tenantMgtRestClient; + private OAuth2RestClient oAuth2RestClientTenant1; + private OAuth2RestClient oAuth2RestClientTenant2; + private SCIM2RestClient scim2RestClientTenant1; + private SCIM2RestClient scim2RestClientTenant2; + private HttpClient client; + + private boolean configurationsRestored = false; + private String tenant1UserId = null; + private String tenant2UserId = null; + private String sessionDataKey; + private String sessionDataKeyConsent; + private String authorizationCode; + private String accessToken; + + @BeforeClass(alwaysRun = true) + private void testInit() throws Exception { + + super.init(); + serverConfigurationManager = new ServerConfigurationManager(isServer); + + // Enable tenant qualified urls/ tenanted sessions and restart the server. + changeISConfigurations(); + + tenantMgtRestClient = new TenantMgtRestClient(serverURL, tenantInfo); + + // Create the test tenants. + addTenant(TENANT_1_DOMAIN, TENANT_1_ADMIN_USERNAME, TENANT_1_ADMIN_PASSWORD, + TENANT_1_ADMIN_TENANT_AWARE_USERNAME); + addTenant(TENANT_2_DOMAIN, TENANT_2_ADMIN_USERNAME, TENANT_2_ADMIN_PASSWORD, + TENANT_2_ADMIN_TENANT_AWARE_USERNAME); + + // Create rest clients. + Tenant tenant1 = getTenantInfo(TENANT_1_DOMAIN); + oAuth2RestClientTenant1 = new OAuth2RestClient(serverURL, tenant1); + scim2RestClientTenant1 = new SCIM2RestClient(serverURL, tenant1); + Tenant tenant2 = getTenantInfo(TENANT_2_DOMAIN); + oAuth2RestClientTenant2 = new OAuth2RestClient(serverURL, tenant2); + scim2RestClientTenant2 = new SCIM2RestClient(serverURL, tenant2); + + // Create http client. + RequestConfig requestConfig = RequestConfig.custom() + .setCookieSpec(CookieSpecs.DEFAULT) + .build(); + Lookup cookieSpecRegistry = RegistryBuilder.create() + .register(CookieSpecs.DEFAULT, new RFC6265CookieSpecProvider()) + .build(); + + client = HttpClientBuilder.create() + .setDefaultCookieStore(new BasicCookieStore()) + .setDefaultRequestConfig(requestConfig) + .setDefaultCookieSpecRegistry(cookieSpecRegistry) + .build(); + } + + @AfterClass(alwaysRun = true) + public void testClear() throws IOException, AutomationUtilException { + + restoreISConfigurations(); + tenantMgtRestClient.closeHttpClient(); + oAuth2RestClientTenant1.closeHttpClient(); + oAuth2RestClientTenant2.closeHttpClient(); + } + + @AfterMethod + public void afterMethod() throws IOException { + + deleteAllApplications(TENANT_1_DOMAIN); + deleteAllApplications(TENANT_2_DOMAIN); + + if (StringUtils.isNotEmpty(tenant1UserId)) { + getSCIMRestClient(TENANT_1_DOMAIN).deleteUser(tenant1UserId); + tenant1UserId = null; + } + if (StringUtils.isNotEmpty(tenant2UserId)) { + getSCIMRestClient(TENANT_2_DOMAIN).deleteUser(tenant2UserId); + tenant2UserId = null; + } + + sessionDataKey = null; + sessionDataKeyConsent = null; + authorizationCode = null; + accessToken = null; + } + + @Test(description = "Create two OAuth apps in the same tenant with same client id.") + public void testCreateAppsWithSameClientIdInSameTenant() throws Exception { + + // Create first app. + ApplicationResponseModel app1 = createApplication(TENANT_1_DOMAIN); + Assert.assertNotNull(app1, "OAuth app creation failed for tenant 1."); + + // Try to create second app with the same client id and expect a conflict. + StatusLine statusLine = createApplicationWithResponse(TENANT_1_DOMAIN); + Assert.assertEquals(statusLine.getStatusCode(), HttpStatus.SC_CONFLICT, "Expected status code not received."); + } + + @Test(description = "Create two OAuth apps in two tenants with the same client id.", + dependsOnMethods = "testCreateAppsWithSameClientIdInSameTenant") + public void testCreateAppsWithSameClientIdInMultipleTenants() throws Exception { + + // Create oauth apps in both tenants. + ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN); + ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN); + + // Assertions for inbound configurations. + Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1."); + OpenIDConnectConfiguration tenant1AppConfig = getOAuthRestClient(TENANT_1_DOMAIN) + .getOIDCInboundDetails(tenant1App.getId()); + Assert.assertNotNull(tenant1AppConfig.getClientId()); + Assert.assertNotNull(tenant1AppConfig.getClientSecret()); + + Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2."); + OpenIDConnectConfiguration tenant2AppConfig = getOAuthRestClient(TENANT_2_DOMAIN) + .getOIDCInboundDetails(tenant2App.getId()); + Assert.assertNotNull(tenant2AppConfig.getClientId()); + Assert.assertNotNull(tenant2AppConfig.getClientSecret()); + } + + @Test(description = "Create two OAuth apps in two tenants with the same client id and retrieve " + + "tenant app by client id.", dependsOnMethods = "testCreateAppsWithSameClientIdInMultipleTenants") + public void testRetrieveTenantAppByClientId() throws JSONException, IOException { + + // Create oauth apps in both tenants. + ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN); + ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN); + + // Assertions for successful app creation. + Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1."); + Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2."); + + // Retrieve app by client id. + List applications = getOAuthRestClient(TENANT_1_DOMAIN) + .getApplicationsByClientId(DUMMY_CLIENT_ID); + Assert.assertEquals(applications.size(), 1); + Assert.assertEquals(applications.get(0).getId(), tenant1App.getId()); + } + + @Test(description = "Create two OAuth apps in two tenants with the same client id and update " + + "inbound configurations of one app.", dependsOnMethods = "testRetrieveTenantAppByClientId") + public void testCreateAppsAndUpdateInboundConfigurationsOfOne() throws Exception { + + // Create oauth apps in both tenants. + ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN); + ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN); + + // Assert for created app. + Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1."); + Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2."); + + // Update inbound configs of tenant 1 app. + OpenIDConnectConfiguration tenant1AppConfig = getOAuthRestClient(TENANT_1_DOMAIN) + .getOIDCInboundDetails(tenant1App.getId()); + List grantTypes = new ArrayList<>(); + Collections.addAll(grantTypes, OAuth2Constant.OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE); + tenant1AppConfig.setGrantTypes(grantTypes); + List callBackUrls = new ArrayList<>(); + Collections.addAll(callBackUrls, "http://localhost:8490/updated/callback"); + tenant1AppConfig.setCallbackURLs(callBackUrls); + + getOAuthRestClient(TENANT_1_DOMAIN).updateInboundDetailsOfApplication(tenant1App.getId(), tenant1AppConfig, + RestBaseClient.OIDC); + + // Assert for updated inbound configs. + OpenIDConnectConfiguration updatedTenant1AppConfig = getOAuthRestClient(TENANT_1_DOMAIN) + .getOIDCInboundDetails(tenant1App.getId()); + Assert.assertNotNull(updatedTenant1AppConfig.getClientId()); + Assert.assertNotNull(updatedTenant1AppConfig.getClientSecret()); + Assert.assertEquals(updatedTenant1AppConfig.getGrantTypes(), grantTypes); + Assert.assertEquals(updatedTenant1AppConfig.getCallbackURLs(), callBackUrls); + + // Assert inbound config of tenant 2 app is not updated. + OpenIDConnectConfiguration tenant2AppConfig = getOAuthRestClient(TENANT_2_DOMAIN) + .getOIDCInboundDetails(tenant2App.getId()); + Assert.assertNotNull(tenant2AppConfig.getClientId()); + Assert.assertNotNull(tenant2AppConfig.getClientSecret()); + + List originalCallBackUrls = new ArrayList<>(); + Collections.addAll(originalCallBackUrls, OAuth2Constant.CALLBACK_URL); + Assert.assertEquals(tenant2AppConfig.getCallbackURLs(), originalCallBackUrls); + + List originalGrantTypes = new ArrayList<>(); + Collections.addAll(originalGrantTypes, OAuth2Constant.OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE, + OAuth2Constant.OAUTH2_GRANT_TYPE_CLIENT_CREDENTIALS, + OAuth2Constant.OAUTH2_GRANT_TYPE_RESOURCE_OWNER, + OAuth2Constant.OAUTH2_GRANT_TYPE_REFRESH_TOKEN); + Assert.assertEquals(tenant2AppConfig.getGrantTypes(), originalGrantTypes); + } + + @Test(description = "Create two OAuth apps in two tenants with the same client id and delete an app " + + "in one tenant.", dependsOnMethods = "testCreateAppsAndUpdateInboundConfigurationsOfOne") + public void testCreateAppsAndDeleteOne() throws Exception { + + // Create oauth apps in both tenants. + ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN); + ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN); + + // Assertions for successful app creation. + Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1."); + Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2."); + + // Delete tenant 1 app and assert for tenant 2 app. + getOAuthRestClient(TENANT_1_DOMAIN).deleteApplication(tenant1App.getId()); + ApplicationListResponse tenant1Apps = getOAuthRestClient(TENANT_1_DOMAIN).getAllApplications(); + Assert.assertEquals(tenant1Apps.getApplications().size(), 0); + ApplicationListResponse tenant2Apps = getOAuthRestClient(TENANT_2_DOMAIN).getAllApplications(); + Assert.assertEquals(tenant2Apps.getApplications().size(), 1); + } + + @Test(description = "Create two OAuth apps in two tenants with the same client id and delete " + + "inbound configurations of one app.", dependsOnMethods = "testCreateAppsAndDeleteOne") + public void testCreateAppsAndDeleteInboundConfigurationsOfOne() throws Exception { + + // Create oauth apps in both tenants. + ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN); + ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN); + + // Assertions for successful app creation. + Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1."); + Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2."); + + // Delete OIDC inbound configurations of tenant 1 app. + getOAuthRestClient(TENANT_1_DOMAIN).deleteInboundConfiguration(tenant1App.getId(), RestBaseClient.OIDC); + + // Try to retrieve inbound configs of tenant 1 app and expect an exception. + try { + OpenIDConnectConfiguration tenant1AppConfig = getOAuthRestClient(TENANT_1_DOMAIN) + .getOIDCInboundDetails(tenant1App.getId()); + Assert.fail("Expected exception not received."); + } catch (Exception e) { + Assert.assertNotNull(e.getMessage()); + } + + // Assert inbound config of tenant 2 app is not deleted. + OpenIDConnectConfiguration tenant2AppConfig = getOAuthRestClient(TENANT_2_DOMAIN) + .getOIDCInboundDetails(tenant2App.getId()); + Assert.assertNotNull(tenant2AppConfig.getClientId()); + Assert.assertNotNull(tenant2AppConfig.getClientSecret()); + } + + @Test(description = "Create two OAuth apps in two tenants with the same client id and try to login " + + "with the user for the tenant.", dependsOnMethods = "testCreateAppsAndDeleteInboundConfigurationsOfOne") + public void testOAuthApplicationLoginSuccess() throws Exception { + + // Create oauth apps in both tenants. + ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN); + ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN); + Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1."); + Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2."); + + // Create users. + tenant1UserId = createUser(TENANT_1_DOMAIN); + tenant2UserId = createUser(TENANT_2_DOMAIN); + + // Authenticate. + initiateAuthorizationRequest(true); + authenticateUser(true, TENANT_1_USER_USERNAME, TENANT_1_USER_PASSWORD, TENANT_1_DOMAIN); + performConsentApproval(true, TENANT_1_DOMAIN); + generateAuthzCodeAccessToken(true, TENANT_1_DOMAIN); + introspectActiveAccessToken(TENANT_1_DOMAIN, TENANT_1_ADMIN_USERNAME, TENANT_1_ADMIN_PASSWORD); + } + + @Test(description = "Create two OAuth apps in two tenants with the same client id and try to login " + + "with the user from the other tenant.", dependsOnMethods = "testOAuthApplicationLoginSuccess", + expectedExceptions = AssertionError.class) + public void testOAuthApplicationLoginIncorrectTenant() throws Exception { + + // Create oauth apps in both tenants. + ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN); + ApplicationResponseModel tenant2App = createApplication(TENANT_2_DOMAIN); + Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1."); + Assert.assertNotNull(tenant2App, "OAuth app creation failed for tenant 2."); + + // Create users. + tenant1UserId = createUser(TENANT_1_DOMAIN); + tenant2UserId = createUser(TENANT_2_DOMAIN); + + // Authenticate. + initiateAuthorizationRequest(true); + authenticateUser(true, TENANT_2_USER_USERNAME, TENANT_2_USER_PASSWORD, TENANT_1_DOMAIN); + Assert.fail("Expected exception not received."); + } + + @Test(description = "Create an OAuth app in tenant 1 and try to login when tenant qualified urls are disabled.", + dependsOnMethods = "testOAuthApplicationLoginIncorrectTenant") + public void testOAuthApplicationLoginWhenTenantQualifiedUrlsDisabled() throws Exception { + + // Restore the server back to the initial state (tenant qualified urls disabled). + restoreISConfigurations(); + + // Create oauth app and add user. + ApplicationResponseModel tenant1App = createApplication(TENANT_1_DOMAIN); + Assert.assertNotNull(tenant1App, "OAuth app creation failed for tenant 1."); + tenant1UserId = createUser(TENANT_1_DOMAIN); + + // Authenticate. + initiateAuthorizationRequest(false); + authenticateUser(false, TENANT_1_USER_USERNAME + "@" + TENANT_1_DOMAIN, TENANT_1_USER_PASSWORD, + TENANT_1_DOMAIN); + performConsentApproval(false, TENANT_1_DOMAIN); + generateAuthzCodeAccessToken(false, TENANT_1_DOMAIN); + introspectActiveAccessToken(TENANT_1_DOMAIN, TENANT_1_ADMIN_USERNAME, TENANT_1_ADMIN_PASSWORD); + } + + private void changeISConfigurations() throws AutomationUtilException, IOException { + + String carbonHome = Utils.getResidentCarbonHome(); + File defaultTomlFile = getDeploymentTomlFile(carbonHome); + File modifiedConfigFile = new File(getISResourceLocation() + + File.separator + "tenant.qualified" + File.separator + + "tenant_qualified_url_tenanted_sessions_enabled.toml"); + serverConfigurationManager.applyConfiguration(modifiedConfigFile, defaultTomlFile, true, true); + } + + private void restoreISConfigurations() throws AutomationUtilException, IOException { + + if (configurationsRestored) { + return; + } + String carbonHome = Utils.getResidentCarbonHome(); + File defaultTomlFile = getDeploymentTomlFile(carbonHome); + File modifiedConfigFile = new File(getISResourceLocation() + + File.separator + "tenant.qualified" + File.separator + + "tenant_qualified_url_tenanted_sessions_default.toml"); + serverConfigurationManager.applyConfiguration(modifiedConfigFile, defaultTomlFile, true, true); + configurationsRestored = true; + } + + private void addTenant(String tenantDomain, String adminUsername, String adminPassword, + String adminTenantAwareUsername) throws Exception { + + Owner tenantOwner = new Owner(); + tenantOwner.setUsername(adminTenantAwareUsername); + tenantOwner.setPassword(adminPassword); + tenantOwner.setEmail(adminUsername); + tenantOwner.setFirstname("FirstName"); + tenantOwner.setLastname("LastName"); + tenantOwner.setProvisioningMethod("inline-password"); + + TenantModel tenantReqModel = new TenantModel(); + tenantReqModel.setDomain(tenantDomain); + tenantReqModel.addOwnersItem(tenantOwner); + String tenantId = tenantMgtRestClient.addTenant(tenantReqModel); + Assert.assertNotNull(tenantId, "Tenant creation failed for " + tenantDomain); + } + + private Tenant getTenantInfo(String tenantDomain) { + + User tenantAdmin = new User(); + if (StringUtils.equals(tenantDomain, TENANT_1_DOMAIN)) { + tenantAdmin.setUserName(TENANT_1_ADMIN_USERNAME); + tenantAdmin.setPassword(TENANT_1_ADMIN_PASSWORD); + } else if (StringUtils.equals(tenantDomain, TENANT_2_DOMAIN)) { + tenantAdmin.setUserName(TENANT_2_ADMIN_USERNAME); + tenantAdmin.setPassword(TENANT_2_ADMIN_PASSWORD); + } + + Tenant tenant = new Tenant(); + tenant.setDomain(tenantDomain); + tenant.setContextUser(tenantAdmin); + return tenant; + } + + private OAuth2RestClient getOAuthRestClient(String tenantDomain) { + + OAuth2RestClient restClient; + if (StringUtils.equals(tenantDomain, TENANT_1_DOMAIN)) { + restClient = oAuth2RestClientTenant1; + } else { + restClient = oAuth2RestClientTenant2; + } + return restClient; + } + + private SCIM2RestClient getSCIMRestClient(String tenantDomain) { + + SCIM2RestClient restClient; + if (StringUtils.equals(tenantDomain, TENANT_1_DOMAIN)) { + restClient = scim2RestClientTenant1; + } else { + restClient = scim2RestClientTenant2; + } + return restClient; + } + + private ApplicationModel getApplicationModel() { + + OpenIDConnectConfiguration oidcConfig = new OpenIDConnectConfiguration(); + oidcConfig.setClientId(DUMMY_CLIENT_ID); + oidcConfig.setClientSecret(DUMMY_CLIENT_SECRET); + oidcConfig.setPublicClient(false); + + List grantTypes = new ArrayList<>(); + Collections.addAll(grantTypes, OAuth2Constant.OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE, + OAuth2Constant.OAUTH2_GRANT_TYPE_CLIENT_CREDENTIALS, + OAuth2Constant.OAUTH2_GRANT_TYPE_RESOURCE_OWNER, + OAuth2Constant.OAUTH2_GRANT_TYPE_REFRESH_TOKEN); + oidcConfig.setGrantTypes(grantTypes); + + List callBackUrls = new ArrayList<>(); + Collections.addAll(callBackUrls, OAuth2Constant.CALLBACK_URL); + oidcConfig.setCallbackURLs(callBackUrls); + + InboundProtocols inboundProtocolsConfig = new InboundProtocols(); + inboundProtocolsConfig.setOidc(oidcConfig); + + ApplicationModel application = new ApplicationModel(); + application.setName(OAuth2Constant.OAUTH_APPLICATION_NAME); + application.setInboundProtocolConfiguration(inboundProtocolsConfig); + application.setClaimConfiguration(setApplicationClaimConfig()); + + return application; + } + + private ApplicationResponseModel createApplication(String tenantDomain) throws JSONException, IOException { + + OAuth2RestClient restClient = getOAuthRestClient(tenantDomain); + String appId = restClient.createApplication(getApplicationModel()); + return restClient.getApplication(appId); + } + + private void deleteAllApplications(String tenantDomain) throws IOException { + + OAuth2RestClient restClient = getOAuthRestClient(tenantDomain); + ApplicationListResponse applications = restClient.getAllApplications(); + for (ApplicationListItem application : applications.getApplications()) { + restClient.deleteApplication(application.getId()); + } + } + + private StatusLine createApplicationWithResponse(String tenantDomain) + throws JSONException, IOException { + + OAuth2RestClient restClient = getOAuthRestClient(tenantDomain); + return restClient.createApplicationWithResponse(getApplicationModel()); + } + + private String createUser(String tenantDomain) throws Exception { + + SCIM2RestClient restClient = getSCIMRestClient(tenantDomain); + UserObject user = new UserObject(); + + if (StringUtils.equals(tenantDomain, TENANT_1_DOMAIN)) { + user.setUserName(TENANT_1_USER_USERNAME); + user.setPassword(TENANT_1_USER_PASSWORD); + user.addEmail(new Email().value(TENANT_1_USER_EMAIL)); + } else { + user.setUserName(TENANT_2_USER_USERNAME); + user.setPassword(TENANT_2_USER_PASSWORD); + user.addEmail(new Email().value(TENANT_2_USER_EMAIL)); + } + + return restClient.createUser(user); + } + + /** + * Playground app will initiate authorization request to IS and obtain session data key. + * + * @param tenantQualified Whether tenant qualified urls are enabled. + * @throws IOException IOException. + */ + private void initiateAuthorizationRequest(boolean tenantQualified) throws IOException { + + List urlParameters = getOIDCInitiationRequestParams(tenantQualified); + HttpResponse response = sendPostRequestWithParameters(client, urlParameters, + OAuth2Constant.AUTHORIZED_USER_URL); + Assert.assertNotNull(response, "Authorization response is null"); + + Header locationHeader = response.getFirstHeader(OAuth2Constant.HTTP_RESPONSE_HEADER_LOCATION); + Assert.assertNotNull(locationHeader, "Authorization response header is null."); + + EntityUtils.consume(response.getEntity()); + response = sendGetRequest(client, locationHeader.getValue()); + sessionDataKey = Utils.extractDataFromResponse(response, CommonConstants.SESSION_DATA_KEY, 1); + EntityUtils.consume(response.getEntity()); + } + + private List getOIDCInitiationRequestParams(boolean tenantQualified) { + + List urlParameters = new ArrayList<>(); + urlParameters.add(new BasicNameValuePair("grantType", OAuth2Constant.OAUTH2_GRANT_TYPE_CODE)); + urlParameters.add(new BasicNameValuePair("consumerKey", DUMMY_CLIENT_ID)); + urlParameters.add(new BasicNameValuePair("callbackurl", OAuth2Constant.CALLBACK_URL)); + if (tenantQualified) { + urlParameters.add(new BasicNameValuePair("authorizeEndpoint", TENANT_1_AUTHORIZE_URL)); + } else { + urlParameters.add(new BasicNameValuePair("authorizeEndpoint", OAuth2Constant.AUTHORIZE_ENDPOINT_URL)); + } + urlParameters.add(new BasicNameValuePair("authorize", OAuth2Constant.AUTHORIZE_PARAM)); + urlParameters.add(new BasicNameValuePair("scope", OAuth2Constant.OAUTH2_SCOPE_OPENID + " " + + OAuth2Constant.OAUTH2_SCOPE_EMAIL)); + + return urlParameters; + } + + /** + * Provide user credentials and authenticate to the system. + * + * @param tenantQualified Whether tenant qualified urls are enabled. + * @param username Username of the user. + * @param password Password of the user. + * @param tenantDomain Tenant domain. + * @throws IOException IOException. + */ + private void authenticateUser(boolean tenantQualified, String username, String password, String tenantDomain) + throws Exception { + + // Pass user credentials to commonauth endpoint and authenticate the user. + HttpResponse response; + if (tenantQualified) { + response = sendLoginPostForCustomUsers(client, sessionDataKey, username, password, tenantDomain); + } else { + response = sendLoginPostForCustomUsers(client, sessionDataKey, username, password); + } + Assert.assertNotNull(response, "OIDC login request response is null."); + + Header locationHeader = response.getFirstHeader(OAuth2Constant.HTTP_RESPONSE_HEADER_LOCATION); + Assert.assertNotNull(locationHeader, "OIDC login response header is null."); + EntityUtils.consume(response.getEntity()); + + // Get the sessionDatakeyConsent from the redirection after authenticating the user. + response = sendGetRequest(client, locationHeader.getValue()); + Map keyPositionMap = new HashMap<>(1); + keyPositionMap.put("name=\"sessionDataKeyConsent\"", 1); + List keyValues = DataExtractUtil + .extractSessionConsentDataFromResponse(response, keyPositionMap); + Assert.assertNotNull(keyValues, "SessionDataKeyConsent keyValues map is null."); + sessionDataKeyConsent = keyValues.get(0).getValue(); + Assert.assertNotNull(sessionDataKeyConsent, "sessionDataKeyConsent is null."); + EntityUtils.consume(response.getEntity()); + } + + /** + * Approve the consent. + * + * @param tenantQualified Whether tenant qualified urls are enabled. + * @param tenantDomain Tenant domain. + * @throws IOException IOException. + */ + private void performConsentApproval(boolean tenantQualified, String tenantDomain) throws IOException { + + HttpResponse response; + if (tenantQualified) { + response = sendApprovalPostWithConsent(client, sessionDataKeyConsent, null, tenantDomain); + } else { + response = sendApprovalPostWithConsent(client, sessionDataKeyConsent, null); + } + Assert.assertNotNull(response, "OIDC consent approval request response is null."); + Header locationHeader = response.getFirstHeader(OAuth2Constant.HTTP_RESPONSE_HEADER_LOCATION); + Assert.assertNotNull(locationHeader, "OIDC consent approval request location header is null."); + EntityUtils.consume(response.getEntity()); + + // Get authorization code flow. + String[] queryParams = new URL(locationHeader.getValue()).getQuery().split("&"); + Assert.assertNotEquals(queryParams.length, 0, "Authorization code not received."); + for (String param : queryParams) { + if (param.contains(OAuth2Constant.OAUTH2_GRANT_TYPE_CODE)) { + authorizationCode = param.split("=")[1]; + } + } + Assert.assertNotNull(authorizationCode, "Authorization code not received."); + } + + /** + * Exchange authorization code and get access token. + * + * @param tenantQualified Whether tenant qualified urls are enabled. + * @param tenantDomain Tenant domain. + * @throws Exception IOException. + */ + private void generateAuthzCodeAccessToken(boolean tenantQualified, String tenantDomain) throws Exception { + + List urlParameters = new ArrayList<>(); + urlParameters.add(new BasicNameValuePair(OAuth2Constant.GRANT_TYPE_NAME, + OAuth2Constant.OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE)); + urlParameters.add(new BasicNameValuePair(OAuth2Constant.OAUTH2_REDIRECT_URI, OAuth2Constant.CALLBACK_URL)); + urlParameters.add(new BasicNameValuePair(OAuth2Constant.AUTHORIZATION_CODE_NAME, authorizationCode)); + + String url; + if (tenantQualified) { + url = OAuth2Constant.TENANT_TOKEN_ENDPOINT.replace(OAuth2Constant.TENANT_PLACEHOLDER, tenantDomain); + } else { + url = OAuth2Constant.ACCESS_TOKEN_ENDPOINT; + } + JSONObject jsonResponse = responseObject(url, urlParameters, DUMMY_CLIENT_ID, DUMMY_CLIENT_SECRET); + Assert.assertNotNull(jsonResponse.get(OAuth2Constant.ACCESS_TOKEN), "Access token is null."); + Assert.assertNotNull(jsonResponse.get(OAuth2Constant.REFRESH_TOKEN), "Refresh token is null."); + accessToken = (String) jsonResponse.get(OAuth2Constant.ACCESS_TOKEN); + } + + /** + * Introspect the obtained access token and it should be an active token. + * + * @param tenantDomain Tenant domain. + * @throws Exception Exception. + */ + private void introspectActiveAccessToken(String tenantDomain, String adminUsername, String adminPassword) + throws Exception { + + String url = OAuth2Constant.TENANT_INTROSPECT_ENDPOINT.replace( + OAuth2Constant.TENANT_PLACEHOLDER, tenantDomain); + JSONObject object = introspectTokenWithTenant(client, accessToken, url, adminUsername, adminPassword); + Assert.assertEquals(object.get("active"), true); + } + + /** + * Build post request and return json response object. + * + * @param endpoint Endpoint. + * @param postParameters postParameters. + * @param key Basic authentication key. + * @param secret Basic authentication secret. + * @return JSON object of the response. + * @throws Exception Exception. + */ + private JSONObject responseObject(String endpoint, List postParameters, String key, String secret) + throws Exception { + + HttpPost httpPost = new HttpPost(endpoint); + httpPost.setHeader("Authorization", "Basic " + getBase64EncodedString(key, secret)); + httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded"); + httpPost.setEntity(new UrlEncodedFormEntity(postParameters)); + HttpResponse response = client.execute(httpPost); + + String responseString = EntityUtils.toString(response.getEntity(), "UTF-8"); + EntityUtils.consume(response.getEntity()); + JSONParser parser = new JSONParser(); + JSONObject json = (JSONObject) parser.parse(responseString); + if (json == null) { + throw new Exception("Error occurred while getting the response."); + } + + return json; + } +} diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/ApplicationListItem.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/ApplicationListItem.java index 8f291e49d9e..cbc0983a98e 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/ApplicationListItem.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/ApplicationListItem.java @@ -30,6 +30,7 @@ public class ApplicationListItem { private String description; private String image; private String accessUrl; + private String clientId; @XmlType(name="AccessEnum") @XmlEnum(String.class) @@ -192,7 +193,24 @@ public void setSelf(String self) { this.self = self; } + public ApplicationListItem clientId(String clientId) { + this.clientId = clientId; + return this; + } + + @ApiModelProperty(example = "clientId", value = "") + @JsonProperty("clientId") + @Valid + public String getClientId() { + + return clientId; + } + + public void setClientId(String clientId) { + + this.clientId = clientId; + } @Override public boolean equals(java.lang.Object o) { diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java index 1117de8bf03..00e8978e71b 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java @@ -22,6 +22,7 @@ import io.restassured.http.ContentType; import org.apache.commons.codec.binary.Base64; import org.apache.http.Header; +import org.apache.http.StatusLine; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.message.BasicHeader; import org.apache.http.util.EntityUtils; @@ -30,6 +31,7 @@ import org.wso2.carbon.automation.engine.context.beans.Tenant; import org.wso2.carbon.utils.StringUtils; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; +import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationListItem; import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationListResponse; import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationModel; import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationPatchModel; @@ -40,6 +42,7 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.List; public class OAuth2RestClient extends RestBaseClient { @@ -74,6 +77,21 @@ public String createApplication(ApplicationModel application) throws IOException } } + /** + * Create an Application. + * + * @param application Application Model with application creation details. + * @return Application creation response. + */ + public StatusLine createApplicationWithResponse(ApplicationModel application) throws IOException, JSONException { + + String jsonRequest = toJSONString(application); + try (CloseableHttpResponse response = getResponseOfHttpPost(applicationManagementApiBasePath, jsonRequest, + getHeaders())) { + return response.getStatusLine(); + } + } + /** * Get Application details * @@ -91,6 +109,27 @@ public ApplicationResponseModel getApplication(String appId) throws IOException } } + /** + * Get Application details by client id. + * + * @param clientId Client id of the application. + * @return Application list. + * @throws IOException Error when getting the response. + */ + public List getApplicationsByClientId(String clientId) throws IOException { + + String endPointUrl = applicationManagementApiBasePath + "?filter=clientId eq " + clientId; + endPointUrl = endPointUrl.replace(" ", "%20"); + + try (CloseableHttpResponse response = getResponseOfHttpGet(endPointUrl, getHeaders())) { + String responseBody = EntityUtils.toString(response.getEntity()); + + ObjectMapper jsonWriter = new ObjectMapper(new JsonFactory()); + ApplicationListResponse applications = jsonWriter.readValue(responseBody, ApplicationListResponse.class); + return applications.getApplications(); + } + } + /** * Update an existing application * diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/user/export/UserInfoExportTestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/user/export/UserInfoExportTestCase.java index 7b5be0134e8..57ca37dce9a 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/user/export/UserInfoExportTestCase.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/user/export/UserInfoExportTestCase.java @@ -51,11 +51,10 @@ public class UserInfoExportTestCase extends ISIntegrationTest { private static final String PI_INFO = "pi-info/"; private static final String ME = "me"; private static final String RESOURCE_PATH = "/api/identity/user/v1.0/"; - private static final String USERNAME_CLAIM_URI = "http://wso2.org/claims/username"; + private static final String GROUPS_ATTRIBUTE = "groups"; private HttpClient client; private String username; - private String tenantAwareUsername; private String password; private String tenant; @@ -64,7 +63,6 @@ public UserInfoExportTestCase(TestUserMode userMode) throws Exception { AutomationContext context = new AutomationContext("IDENTITY", userMode); this.username = context.getContextTenant().getTenantAdmin().getUserName(); - this.tenantAwareUsername = context.getContextTenant().getTenantAdmin().getUserNameWithoutDomain(); this.password = context.getContextTenant().getTenantAdmin().getPassword(); this.tenant = context.getContextTenant().getDomain(); } @@ -97,14 +95,13 @@ public void testExportUserInfo() throws IOException { Object responseObj = JSONValue.parse(rd); EntityUtils.consume(response.getEntity()); - Object basicObj = ((JSONObject) responseObj).get("basic"); - if (basicObj == null) { + Object userProfileObj = ((JSONObject) responseObj).get("user_profile"); + if (userProfileObj == null) { Assert.fail(); } else { - JSONObject basic = (JSONObject) basicObj; - String username = basic.get(USERNAME_CLAIM_URI).toString(); - //TODO tenant aware username is coming. is this okay? - Assert.assertEquals(username, this.tenantAwareUsername); + JSONObject userProfile = (JSONObject) userProfileObj; + String groups = userProfile.get(GROUPS_ATTRIBUTE).toString(); + Assert.assertNotNull(groups); } } @@ -121,10 +118,9 @@ public void testExportUserInfoMe() throws IOException { Object responseObj = JSONValue.parse(rd); EntityUtils.consume(response.getEntity()); - JSONObject basic = (JSONObject)((JSONObject) responseObj).get("basic"); - String username = basic.get(USERNAME_CLAIM_URI).toString(); - //TODO tenant aware username is coming. is this okay? - Assert.assertEquals(username, this.tenantAwareUsername); + JSONObject userProfile = (JSONObject)((JSONObject) responseObj).get("user_profile"); + String groups = userProfile.get(GROUPS_ATTRIBUTE).toString(); + Assert.assertNotNull(groups); } private String getPiInfoPath() { diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java index 286e6a9eebe..67434f4c365 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java @@ -120,6 +120,14 @@ public final class OAuth2Constant { public static final String RESPONSE_TYPE_CODE_ID_TOKEN = "code id_token"; + // Tenanted urls. + public final static String TENANT_PLACEHOLDER = ""; + public final static String TENANT_COMMON_AUTH_URL = "https://localhost:9853/t//commonauth"; + public final static String TENANT_APPROVAL_URL = "https://localhost:9853/t//oauth2/authorize"; + public final static String TENANT_TOKEN_ENDPOINT = "https://localhost:9853/t//oauth2/token"; + public static final String TENANT_INTROSPECT_ENDPOINT = + "https://localhost:9853/t//oauth2/introspect"; + public static final class PlaygroundAppPaths { public static final String callBackPath = "/oauth2client"; diff --git a/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/IS/tenant.qualified/tenant_qualified_url_tenanted_sessions_default.toml b/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/IS/tenant.qualified/tenant_qualified_url_tenanted_sessions_default.toml new file mode 100644 index 00000000000..2a6ee115a7e --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/IS/tenant.qualified/tenant_qualified_url_tenanted_sessions_default.toml @@ -0,0 +1,39 @@ +[server] +hostname = "localhost" +node_ip = "127.0.0.1" +base_path = "https://$ref{server.hostname}:${carbon.management.port}" + +[super_admin] +username = "admin" +password = "admin" +create_admin_account = true + +[user_store] +type = "database_unique_id" + +[database.identity_db] +driver = "$env{IDENTITY_DATABASE_DRIVER}" +url = "$env{IDENTITY_DATABASE_URL}" +username = "$env{IDENTITY_DATABASE_USERNAME}" +password = "$env{IDENTITY_DATABASE_PASSWORD}" + +[database.shared_db] +driver = "$env{SHARED_DATABASE_DRIVER}" +url = "$env{SHARED_DATABASE_URL}" +username = "$env{SHARED_DATABASE_USERNAME}" +password = "$env{SHARED_DATABASE_PASSWORD}" + +[keystore.primary] +file_name = "wso2carbon.jks" +password = "wso2carbon" + +[truststore] +file_name = "client-truststore.jks" +password = "wso2carbon" +type = "JKS" + +[account_recovery.endpoint.auth] +hash = "66cd9688a2ae068244ea01e70f0e230f5623b7fa4cdecb65070a09ec06452262" + +[identity.auth_framework.endpoint] +app_password = "dashboard" diff --git a/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/IS/tenant.qualified/tenant_qualified_url_tenanted_sessions_enabled.toml b/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/IS/tenant.qualified/tenant_qualified_url_tenanted_sessions_enabled.toml new file mode 100644 index 00000000000..8dfe125c8c8 --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/IS/tenant.qualified/tenant_qualified_url_tenanted_sessions_enabled.toml @@ -0,0 +1,43 @@ +[server] +hostname = "localhost" +node_ip = "127.0.0.1" +base_path = "https://$ref{server.hostname}:${carbon.management.port}" + +[super_admin] +username = "admin" +password = "admin" +create_admin_account = true + +[user_store] +type = "database_unique_id" + +[database.identity_db] +driver = "$env{IDENTITY_DATABASE_DRIVER}" +url = "$env{IDENTITY_DATABASE_URL}" +username = "$env{IDENTITY_DATABASE_USERNAME}" +password = "$env{IDENTITY_DATABASE_PASSWORD}" + +[database.shared_db] +driver = "$env{SHARED_DATABASE_DRIVER}" +url = "$env{SHARED_DATABASE_URL}" +username = "$env{SHARED_DATABASE_USERNAME}" +password = "$env{SHARED_DATABASE_PASSWORD}" + +[keystore.primary] +file_name = "wso2carbon.jks" +password = "wso2carbon" + +[truststore] +file_name = "client-truststore.jks" +password = "wso2carbon" +type = "JKS" + +[account_recovery.endpoint.auth] +hash = "66cd9688a2ae068244ea01e70f0e230f5623b7fa4cdecb65070a09ec06452262" + +[identity.auth_framework.endpoint] +app_password = "dashboard" + +[tenant_context] +enable_tenant_qualified_urls = true +enable_tenanted_sessions = true diff --git a/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml b/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml index feb491d40aa..88cd04b199e 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml +++ b/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml @@ -346,6 +346,7 @@ + diff --git a/modules/p2-profile-gen/pom.xml b/modules/p2-profile-gen/pom.xml index 1f3ae282cf1..217c07bf683 100644 --- a/modules/p2-profile-gen/pom.xml +++ b/modules/p2-profile-gen/pom.xml @@ -263,6 +263,9 @@ org.wso2.carbon.extension.identity.authenticator.outbound.emailotp:org.wso2.carbon.identity.authenticator.emailotp.feature:${authenticator.emailotp.version} + + org.wso2.carbon.extension.identity.oauth.addons:org.wso2.carbon.identity.oauth2.token.handler.clientauth.jwt.feature:${identity.oauth.addons.version} + org.wso2.carbon.identity.user.ws:org.wso2.carbon.um.ws.service.feature:${identity.user.ws.version} @@ -1102,6 +1105,10 @@ org.wso2.carbon.identity.authenticator.emailotp.feature.group ${authenticator.emailotp.version} + + org.wso2.carbon.identity.oauth2.token.handler.clientauth.jwt.feature.group + ${identity.oauth.addons.version} + org.wso2.carbon.identity.application.authenticator.oidc.server.feature.group @@ -1673,6 +1680,10 @@ org.wso2.carbon.identity.authenticator.emailotp.feature.group ${authenticator.emailotp.version} + + org.wso2.carbon.identity.oauth2.token.handler.clientauth.jwt.feature.group + ${identity.oauth.addons.version} + org.wso2.carbon.extension.identity.verification.provider.feature.group ${identity.verification.version} @@ -2148,6 +2159,10 @@ org.wso2.carbon.identity.authenticator.emailotp.feature.group ${authenticator.emailotp.version} + + org.wso2.carbon.identity.oauth2.token.handler.clientauth.jwt.feature.group + ${identity.oauth.addons.version} + org.wso2.carbon.extension.identity.verification.provider.feature.group ${identity.verification.version} diff --git a/pom.xml b/pom.xml index e7e5d225941..5a7aa3dd533 100755 --- a/pom.xml +++ b/pom.xml @@ -2049,6 +2049,12 @@ org.wso2.carbon.extension.identity.verification.ui ${identity.verification.version} + + org.wso2.carbon.extension.identity.oauth.addons + org.wso2.carbon.identity.oauth2.token.handler.clientauth.jwt + ${identity.oauth.addons.version} + + org.wso2.msf4j msf4j-core @@ -2292,7 +2298,7 @@ - 5.25.356 + 5.25.375 [5.14.67, 6.0.0] @@ -2303,18 +2309,18 @@ 2.5.2 - 1.8.73 + 1.8.76 5.8.5 5.5.0 5.5.0 - 1.8.21 + 1.8.22 5.11.23 - 6.11.127 + 6.11.130 5.9.5 5.10.16 5.7.4 @@ -2340,7 +2346,7 @@ 1.8.12 - 1.7.16 + 1.7.17 @@ -2372,7 +2378,7 @@ 2.0.3 - 6.7.23 + 6.7.26 5.3.39 5.4.2 @@ -2399,8 +2405,8 @@ 1.0.14 1.0.0 - 1.3.72 - 1.0.63 + 1.3.76 + 1.0.67 1.1.21 1.1.9 @@ -2408,12 +2414,12 @@ 0.1.4 - 1.0.5 + 1.0.7 2.0.13 1.3.22 - 1.2.84 + 1.2.86 5.5.9 5.5.7 @@ -2423,9 +2429,9 @@ 1.2.36 - 2.0.12 - 2.0.6 - 2.0.5 + 2.0.24 + 2.0.9 + 2.0.7 1.6.373