From b6b9f89f6fd74c15076298bfa2abcb7127626b34 Mon Sep 17 00:00:00 2001 From: Shenali Date: Tue, 13 Aug 2024 17:43:56 +0530 Subject: [PATCH] Add integration tests for pre issue access token password grant Add test cases Improve formatting Improve formatting Clean the code Restructre the architecture Reformat the code Revert unneccessary formatting Add new application logic Addressed the comments Integrate mock service Increase code readability Reformat the code Restructure the base architecture Add new test case to testng file Fix formatting issues Update the implementation Reset formatting Fix formatting issues Fix formatting issues Remove data providers Update snakeyaml dependency Reformat code Update the test atEnd method Change the order of execution of atEnd method Change the order of execution of atEnd method --- .../tests-integration/tests-backend/pom.xml | 5 + .../test/actions/ActionsBaseTestCase.java | 77 ++++ ...IssueAccessTokenPasswordGrantTestCase.java | 373 ++++++++++++++++++ .../integration/test/mocks/MockServer.java | 145 +++++++ .../OAuth2ServiceAbstractIntegrationTest.java | 119 +++++- .../v1/model/AuthorizedDomainAPIResponse.java | 168 ++++++++ .../v1/model/DomainAPICreationModel.java | 133 +++++++ .../test/restclients/ActionsRestClient.java | 146 +++++++ .../test/restclients/OAuth2RestClient.java | 36 ++ .../src/test/resources/testng.xml | 1 + pom.xml | 14 + 11 files changed, 1216 insertions(+), 1 deletion(-) create mode 100644 modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/actions/ActionsBaseTestCase.java create mode 100644 modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/actions/PreIssueAccessTokenPasswordGrantTestCase.java create mode 100644 modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/mocks/MockServer.java create mode 100644 modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/AuthorizedDomainAPIResponse.java create mode 100644 modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/DomainAPICreationModel.java create mode 100644 modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/ActionsRestClient.java diff --git a/modules/integration/tests-integration/tests-backend/pom.xml b/modules/integration/tests-integration/tests-backend/pom.xml index 08f1be8d61e..20cdd8663f0 100644 --- a/modules/integration/tests-integration/tests-backend/pom.xml +++ b/modules/integration/tests-integration/tests-backend/pom.xml @@ -647,6 +647,11 @@ org.apache.httpcomponents httpclient + + org.wiremock + wiremock + test + io.rest-assured rest-assured diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/actions/ActionsBaseTestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/actions/ActionsBaseTestCase.java new file mode 100644 index 00000000000..fdbed9d8739 --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/actions/ActionsBaseTestCase.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024, 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.actions; + +import org.wso2.carbon.automation.engine.context.TestUserMode; +import org.wso2.identity.integration.test.oauth2.OAuth2ServiceAbstractIntegrationTest; +import org.wso2.identity.integration.test.rest.api.server.action.management.v1.model.ActionModel; +import org.wso2.identity.integration.test.restclients.ActionsRestClient; + +import java.io.IOException; + +/** + * Base test case for action-related tests. + * This class extends {@link OAuth2ServiceAbstractIntegrationTest} and provides the necessary setup + * and utility methods for testing actions via the {@link ActionsRestClient}. + */ +public class ActionsBaseTestCase extends OAuth2ServiceAbstractIntegrationTest { + + protected ActionsRestClient restClient; + + /** + * Initialize the test case. + * + * @param userMode User Mode + * @throws Exception If an error occurred while initializing the clients. + */ + protected void init(TestUserMode userMode) throws Exception { + + super.init(userMode); + + restClient = new ActionsRestClient(serverURL, tenantInfo); + + setSystemproperties(); + } + + /** + * Create action of different types. + * + * @param actionType Type of action + * @param actionModel Request object to create the action + * @return Status code of the action creation + * @throws IOException If an error occurred while creating the action + */ + public String createAction(String actionType, ActionModel actionModel) throws IOException { + + return restClient.createActionType(actionModel, actionType); + } + + /** + * Delete an action. + * + * @param actionType Type of action + * @param actionId ID of the action + * @return Status code of the action creation + * @throws IOException If an error occurred while deleting the action + */ + public int deleteAction(String actionType, String actionId) throws IOException { + + return restClient.deleteActionType(actionType, actionId); + } +} diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/actions/PreIssueAccessTokenPasswordGrantTestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/actions/PreIssueAccessTokenPasswordGrantTestCase.java new file mode 100644 index 00000000000..2f6cbce0011 --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/actions/PreIssueAccessTokenPasswordGrantTestCase.java @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2024, 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.actions; + +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import org.apache.commons.lang.ArrayUtils; +import org.json.JSONException; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import org.wso2.carbon.automation.engine.context.TestUserMode; +import org.wso2.identity.integration.test.mocks.MockServer; +import org.wso2.identity.integration.test.rest.api.server.action.management.v1.model.ActionModel; +import org.wso2.identity.integration.test.rest.api.server.action.management.v1.model.AuthenticationType; +import org.wso2.identity.integration.test.rest.api.server.action.management.v1.model.Endpoint; +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.OpenIDConnectConfiguration; +import org.wso2.identity.integration.test.rest.api.server.roles.v2.model.Audience; +import org.wso2.identity.integration.test.rest.api.server.roles.v2.model.Permission; +import org.wso2.identity.integration.test.rest.api.server.roles.v2.model.RoleV2; +import org.wso2.identity.integration.test.rest.api.user.common.model.Email; +import org.wso2.identity.integration.test.rest.api.user.common.model.ListObject; +import org.wso2.identity.integration.test.rest.api.user.common.model.Name; +import org.wso2.identity.integration.test.rest.api.user.common.model.PatchOperationRequestObject; +import org.wso2.identity.integration.test.rest.api.user.common.model.RoleItemAddGroupobj; +import org.wso2.identity.integration.test.rest.api.user.common.model.UserObject; +import org.wso2.identity.integration.test.restclients.ActionsRestClient; +import org.wso2.identity.integration.test.restclients.SCIM2RestClient; +import org.wso2.identity.integration.test.utils.CarbonUtils; +import org.wso2.identity.integration.test.utils.OAuth2Constant; + +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Integration test class for testing the pre issue access token flow with password grant. + * This test case extends {@link ActionsBaseTestCase} and focuses on scenarios related + * to scopes and claims modifications through an external service. + */ +public class PreIssueAccessTokenPasswordGrantTestCase extends ActionsBaseTestCase { + + private static final String USERS = "users"; + private static final String USERNAME_PROPERTY = "username"; + private static final String PASSWORD_PROPERTY = "password"; + private static final String TEST_USER = "test_user"; + private static final String ADMIN_WSO2 = "Admin@wso2"; + private static final String TEST_USER_GIVEN = "test_user_given"; + private static final String TEST_USER_GMAIL_COM = "test.user@gmail.com"; + private static final String EXTERNAL_SERVICE_NAME = "TestExternalService"; + private static final String EXTERNAL_SERVICE_URI = "http://localhost:8587/test/action"; + private static final String PRE_ISSUE_ACCESS_TOKEN_API_PATH = "preIssueAccessToken"; + + private static final String PASSWORD_GRANT_TYPE = "password"; + private static final String APPLICATION_AUDIENCE = "APPLICATION"; + private static final String TEST_ROLE_APPLICATION = "test_role_application"; + + private static final String INTERNAL_ACTION_MANAGEMENT_VIEW = "internal_action_mgt_view"; + private static final String INTERNAL_ACTION_MANAGEMENT_CREATE = "internal_action_mgt_create"; + private static final String INTERNAL_ACTION_MANAGEMENT_UPDATE = "internal_action_mgt_update"; + private static final String INTERNAL_ACTION_MANAGEMENT_DELETE = "internal_action_mgt_delete"; + private static final String INTERNAL_ORG_USER_MANAGEMENT_LIST = "internal_org_user_mgt_list"; + private static final String INTERNAL_ORG_USER_MANAGEMENT_VIEW = "internal_org_user_mgt_view"; + private static final String INTERNAL_ORG_USER_MANAGEMENT_CREATE = "internal_org_user_mgt_create"; + private static final String INTERNAL_ORG_USER_MANAGEMENT_UPDATE = "internal_org_user_mgt_update"; + private static final String INTERNAL_ORG_USER_MANAGEMENT_DELETE = "internal_org_user_mgt_delete"; + private static final String INTERNAL_APPLICATION_MANAGEMENT_VIEW = "internal_application_mgt_view"; + private static final String INTERNAL_APPLICATION_MANAGEMENT_UPDATE = "internal_application_mgt_update"; + private static final String INTERNAL_API_RESOURCE_VIEW = "internal_api_resource_view"; + private static final String INTERNAL_API_RESOURCE_CREATE = "internal_api_resource_create"; + private static final String CUSTOM_SCOPE_1 = "test_custom_scope_1"; + private static final String CUSTOM_SCOPE_2 = "test_custom_scope_2"; + private static final String CUSTOM_SCOPE_3 = "test_custom_scope_3"; + private static final String NEW_SCOPE_1 = "new_test_custom_scope_1"; + private static final String NEW_SCOPE_2 = "new_test_custom_scope_2"; + private static final String NEW_SCOPE_3 = "new_test_custom_scope_3"; + private static final String NEW_SCOPE_4 = "replaced_scope"; + + private static final String SCIM2_USERS_API = "/o/scim2/Users"; + private static final String ACTIONS_API = "/api/server/v1/actions"; + private static final String APPLICATION_MANAGEMENT_API = "/api/server/v1/applications"; + private static final String API_RESOURCE_MANAGEMENT_API = "/api/server/v1/api-resources"; + private static final String MOCK_SERVER_ENDPOINT = "/test/action"; + + protected SCIM2RestClient scim2RestClient; + private String accessToken; + private String clientId; + private String actionId; + private String applicationId; + private String domainAPIId; + private String userId; + private String roleId; + private JWTClaimsSet jwtClaims; + + /** + * Initializes Test environment and sets up necessary configurations. + * + * @throws Exception If an error occurs during initialization + */ + @BeforeClass(alwaysRun = true) + public void testInit() throws Exception { + + super.init(TestUserMode.TENANT_USER); + + scim2RestClient = new SCIM2RestClient(serverURL, tenantInfo); + restClient = new ActionsRestClient(serverURL, tenantInfo); + // TODO: Review if ActionsRestClient should be instantiated, or if the superclass initialization is sufficient + + List customScopes = Arrays.asList(CUSTOM_SCOPE_1, CUSTOM_SCOPE_2, CUSTOM_SCOPE_3); + + ApplicationResponseModel application = addApplicationWithGrantType(PASSWORD_GRANT_TYPE); + applicationId = application.getId(); + if (!CarbonUtils.isLegacyAuthzRuntimeEnabled()) { + authorizeSystemAPIs(applicationId, new ArrayList<>(Arrays.asList(SCIM2_USERS_API, ACTIONS_API, + APPLICATION_MANAGEMENT_API, API_RESOURCE_MANAGEMENT_API))); + } + domainAPIId = createDomainAPI(EXTERNAL_SERVICE_NAME, EXTERNAL_SERVICE_URI, customScopes); + authorizeDomainAPIs(applicationId, domainAPIId, customScopes); + + addUserWithRole(applicationId, customScopes); + + MockServer.createMockServer(MOCK_SERVER_ENDPOINT); + actionId = createPreIssueAccessTokenAction(); + + accessToken = retrieveAccessToken(application.getId(), customScopes); + jwtClaims = extractJwtClaims(accessToken); + } + + @AfterClass(alwaysRun = true) + public void atEnd() throws Exception { + + deleteAction(PRE_ISSUE_ACCESS_TOKEN_API_PATH, actionId); + restClient = null; + deleteRole(roleId); + deleteApp(applicationId); + deleteDomainAPI(domainAPIId); + scim2RestClient.deleteUser(userId); + scim2RestClient = null; + MockServer.shutDownMockServer(); + accessToken = null; + jwtClaims = null; + } + + @Test(groups = "wso2.is", description = "Verify the presence of the updated scopes in the access token") + public void testTokenScopeOperations() throws Exception { + + String[] scopes = jwtClaims.getStringClaim("scope").split("\\s+"); + + Assert.assertTrue(ArrayUtils.contains(scopes, NEW_SCOPE_1)); + Assert.assertTrue(ArrayUtils.contains(scopes, NEW_SCOPE_2)); + Assert.assertTrue(ArrayUtils.contains(scopes, NEW_SCOPE_3)); + Assert.assertTrue(ArrayUtils.contains(scopes, NEW_SCOPE_4)); + Assert.assertFalse(ArrayUtils.contains(scopes, CUSTOM_SCOPE_3)); + Assert.assertFalse(ArrayUtils.contains(scopes, CUSTOM_SCOPE_2)); + } + + @Test(groups = "wso2.is", description = "Verify the presence of the updated aud claims in the access token") + public void testTokenAUDClaimOperations() throws Exception { + + String[] audValueArray = jwtClaims.getStringArrayClaim("aud"); + + Assert.assertTrue(ArrayUtils.contains(audValueArray, "zzz1.com")); + Assert.assertTrue(ArrayUtils.contains(audValueArray, "zzz2.com")); + Assert.assertTrue(ArrayUtils.contains(audValueArray, "zzz3.com")); + Assert.assertTrue(ArrayUtils.contains(audValueArray, "zzzR.com")); + Assert.assertFalse(ArrayUtils.contains(audValueArray, clientId)); + } + + @Test(groups = "wso2.is", description = "Verify the presence of the specified custom string claim in the access " + + "token") + public void testTokenStringClaimAddOperation() throws Exception { + + String claimStr = jwtClaims.getStringClaim("custom_claim_string_1"); + Assert.assertEquals(claimStr, "testCustomClaim1"); + + } + + @Test(groups = "wso2.is", description = "Verify the presence of the specified custom number claim in the access " + + "token") + public void testTokenNumberClaimAddOperation() throws Exception { + + Number claimValue = jwtClaims.getIntegerClaim("custom_claim_number_1"); + Assert.assertEquals(claimValue, 78); + + } + + @Test(groups = "wso2.is", description = "Verify the presence of the specified custom boolean claim in the access " + + "token") + public void testTokenBooleanClaimAddOperation() throws Exception { + + Boolean claimValue = jwtClaims.getBooleanClaim("custom_claim_boolean_1"); + Assert.assertTrue(claimValue); + } + + @Test(groups = "wso2.is", description = "Verify the presence of the specified custom string array claim in the " + + "access token") + public void testTokenStringArrayClaimAddOperation() + throws Exception { + + String[] claimArray1 = {"TestCustomClaim1", "TestCustomClaim2", "TestCustomClaim3"}; + + String[] claimArray = jwtClaims.getStringArrayClaim("custom_claim_string_array_1"); + Assert.assertEquals(claimArray, claimArray1); + } + + @Test(groups = "wso2.is", description = "Verify the replacement of the 'expires_in' claim in the access token") + public void testTokenExpiresInClaimReplaceOperation() throws Exception { + + if (jwtClaims.getClaim("expires_in") != null) { + Object expValue = jwtClaims.getLongClaim("expires_in"); + Assert.assertEquals(expValue, 7200); + } + } + + /** + * Creates an action for pre-issuing an access token with basic authentication. + */ + private String createPreIssueAccessTokenAction() { + + AuthenticationType authenticationType = new AuthenticationType(); + authenticationType.setType(AuthenticationType.TypeEnum.BASIC); // todo handle mock server authorization + Map authProperties = new HashMap<>(); + authProperties.put(USERNAME_PROPERTY, TEST_USER); + authProperties.put(PASSWORD_PROPERTY, ADMIN_WSO2); + authenticationType.setProperties(authProperties); + + Endpoint endpoint = new Endpoint(); + endpoint.setUri(EXTERNAL_SERVICE_URI); + endpoint.setAuthentication(authenticationType); + + ActionModel actionModel = new ActionModel(); + actionModel.setName("Access Token Pre Issue"); + actionModel.setDescription("This is a test pre issue access token type"); + actionModel.setEndpoint(endpoint); + + try { + return createAction(PRE_ISSUE_ACCESS_TOKEN_API_PATH, actionModel); + } catch (IOException e) { + throw new RuntimeException("Error while creating pre issue access token " + actionModel.getName()); + } + } + + /** + * Retrieves an access token for the application. + * + * @param applicationId ID of the application + * @param customScopes Custom scopes related to the integrated domain APIs + * @return Access token + * @throws Exception If error occurred wile requesting access token + */ + private String retrieveAccessToken(String applicationId, List customScopes) throws Exception { + + OpenIDConnectConfiguration oidcConfig = getOIDCInboundDetailsOfApplication(applicationId); + clientId = oidcConfig.getClientId(); + String tenantedTokenURI = getTenantQualifiedURL(OAuth2Constant.ACCESS_TOKEN_ENDPOINT, tenantInfo.getDomain()); + + List permissions = new ArrayList<>(); + Collections.addAll(permissions, + new Permission(INTERNAL_ORG_USER_MANAGEMENT_LIST), + new Permission(INTERNAL_ORG_USER_MANAGEMENT_VIEW), + new Permission(INTERNAL_ORG_USER_MANAGEMENT_CREATE), + new Permission(INTERNAL_ORG_USER_MANAGEMENT_UPDATE), + new Permission(INTERNAL_ORG_USER_MANAGEMENT_DELETE) + ); + customScopes.forEach(scope -> permissions.add(new Permission(scope))); + + return requestAccessToken(clientId, oidcConfig.getClientSecret(), tenantedTokenURI, + TEST_USER, ADMIN_WSO2, permissions); + } + + /** + * Extracts the JWT claims set from a given JWT token. + * + * @param jwtToken JWT token from which claims are to be extracted + * @return JWTClaimsSet extracted from the provided JWT token + * @throws ParseException If there is an error in parsing the JWT token + */ + private JWTClaimsSet extractJwtClaims(String jwtToken) throws ParseException { + + SignedJWT signedJWT = SignedJWT.parse(jwtToken); + return signedJWT.getJWTClaimsSet(); + } + + /** + * Adds a user with a role and specific permissions based on custom scopes. + * + * @param appID Application ID to which the role is associated + * @param customScopes The custom scopes based on which permissions are added + * @return A list of permissions that were added to the role + * @throws JSONException If there is an error in processing JSON + * @throws IOException If there is an IO exception during user or role creation + */ + private void addUserWithRole(String appID, List customScopes) throws Exception { + // Creates roles + List permissions = addPermissions(customScopes); + Audience roleAudience = new Audience(APPLICATION_AUDIENCE, appID); + RoleV2 role = new RoleV2(roleAudience, TEST_ROLE_APPLICATION, permissions, Collections.emptyList()); + roleId = addRole(role); + + // Creates user + UserObject userInfo = new UserObject(); + userInfo.setUserName(TEST_USER); + userInfo.setPassword(ADMIN_WSO2); + userInfo.setName(new Name().givenName(TEST_USER_GIVEN)); + userInfo.addEmail(new Email().value(TEST_USER_GMAIL_COM)); + userId = scim2RestClient.createUser(userInfo); + + // Assigns role to the created user + RoleItemAddGroupobj rolePatchReqObject = new RoleItemAddGroupobj(); + rolePatchReqObject.setOp(RoleItemAddGroupobj.OpEnum.ADD); + rolePatchReqObject.setPath(USERS); + rolePatchReqObject.addValue(new ListObject().value(userId)); + scim2RestClient.updateUserRole(new PatchOperationRequestObject().addOperations(rolePatchReqObject), roleId); + } + + /** + * Adds permissions based on the provided custom scopes. + * + * @param customScopes A list of custom scopes to add as permissions + * @return A list of permissions including both predefined and custom scope-based permissions + */ + private List addPermissions(List customScopes) { + + List userPermissions = new ArrayList<>(); + + Collections.addAll(userPermissions, + new Permission(INTERNAL_ACTION_MANAGEMENT_VIEW), + new Permission(INTERNAL_ACTION_MANAGEMENT_CREATE), + new Permission(INTERNAL_ACTION_MANAGEMENT_UPDATE), + new Permission(INTERNAL_ACTION_MANAGEMENT_DELETE), + + new Permission(INTERNAL_ORG_USER_MANAGEMENT_LIST), + new Permission(INTERNAL_ORG_USER_MANAGEMENT_VIEW), + new Permission(INTERNAL_ORG_USER_MANAGEMENT_CREATE), + new Permission(INTERNAL_ORG_USER_MANAGEMENT_UPDATE), + new Permission(INTERNAL_ORG_USER_MANAGEMENT_DELETE), + + new Permission(INTERNAL_APPLICATION_MANAGEMENT_VIEW), + new Permission(INTERNAL_APPLICATION_MANAGEMENT_UPDATE), + + new Permission(INTERNAL_API_RESOURCE_VIEW), + new Permission(INTERNAL_API_RESOURCE_CREATE) + ); + + customScopes.forEach(scope -> userPermissions.add(new Permission(scope))); + + return userPermissions; + } + // TODO: Validate the changes with the "enable_password_grant_enhancements" configuration enabled. +} diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/mocks/MockServer.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/mocks/MockServer.java new file mode 100644 index 00000000000..74fbbaf6e11 --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/mocks/MockServer.java @@ -0,0 +1,145 @@ +package org.wso2.identity.integration.test.mocks; + +import com.github.tomakehurst.wiremock.WireMockServer; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; + +/** + * Provides a mock server using WireMock for testing purposes. + * This class starts a mock server on a specified port and sets up predefined + * responses for POST requests to simulate various operations relation to action execution. + */ +public class MockServer { + + private static WireMockServer wireMockServer; + + /** + * Create a mock server with wiremock. + * + * @throws Exception If an error occurred while creating the server + */ + public static void createMockServer(String mockEndpoint) throws Exception { + + wireMockServer = new WireMockServer(wireMockConfig().port(8587)); + + wireMockServer.start(); + + try { + // TODO: Read the response from a file + // TODO: Filter the response from the action type + String jsonResponse = "{\n" + + " \"actionStatus\": \"SUCCESS\",\n" + + " \"operations\": [\n" + + " {\n" + + " \"op\": \"add\",\n" + + " \"path\": \"/accessToken/scopes/-\",\n" + + " \"value\": \"new_test_custom_scope_1\"\n" + + " },\n" + + " {\n" + + " \"op\": \"add\",\n" + + " \"path\": \"/accessToken/scopes/-\",\n" + + " \"value\": \"new_test_custom_scope_2\"\n" + + " },\n" + + " {\n" + + " \"op\": \"add\",\n" + + " \"path\": \"/accessToken/scopes/-\",\n" + + " \"value\": \"new_test_custom_scope_3\"\n" + + " },\n" + + " {\n" + + " \"op\": \"add\",\n" + + " \"path\": \"/accessToken/claims/aud/-\",\n" + + " \"value\": \"zzz1.com\"\n" + + " },\n" + + " {\n" + + " \"op\": \"add\",\n" + + " \"path\": \"/accessToken/claims/aud/-\",\n" + + " \"value\": \"zzz2.com\"\n" + + " },\n" + + " {\n" + + " \"op\": \"add\",\n" + + " \"path\": \"/accessToken/claims/aud/-\",\n" + + " \"value\": \"zzz3.com\"\n" + + " },\n" + + " {\n" + + " \"op\": \"add\",\n" + + " \"path\": \"/accessToken/claims/-\",\n" + + " \"value\": {\n" + + " \"name\": \"custom_claim_string_1\",\n" + + " \"value\": \"testCustomClaim1\"\n" + + " }\n" + + " },\n" + + " {\n" + + " \"op\": \"add\",\n" + + " \"path\": \"/accessToken/claims/-\",\n" + + " \"value\": {\n" + + " \"name\": \"custom_claim_number_1\",\n" + + " \"value\": 78\n" + + " }\n" + + " },\n" + + " {\n" + + " \"op\": \"add\",\n" + + " \"path\": \"/accessToken/claims/-\",\n" + + " \"value\": {\n" + + " \"name\": \"custom_claim_boolean_1\",\n" + + " \"value\": true\n" + + " }\n" + + " },\n" + + " {\n" + + " \"op\": \"add\",\n" + + " \"path\": \"/accessToken/claims/-\",\n" + + " \"value\": {\n" + + " \"name\": \"custom_claim_string_array_1\",\n" + + " \"value\": [\n" + + " \"TestCustomClaim1\",\n" + + " \"TestCustomClaim2\",\n" + + " \"TestCustomClaim3\"\n" + + " ]\n" + + " }\n" + + " },\n" + + " {\n" + + " \"op\": \"replace\",\n" + + " \"path\": \"/accessToken/scopes/7\",\n" + + " \"value\": \"replaced_scope\"\n" + + " },\n" + + " {\n" + + " \"op\": \"replace\",\n" + + " \"path\": \"/accessToken/claims/aud/-\",\n" + + " \"value\": \"zzzR.com\"\n" + + " },\n" + + " {\n" + + " \"op\": \"remove\",\n" + + " \"path\": \"/accessToken/scopes/6\"\n" + + " },\n" + + " {\n" + + " \"op\": \"remove\",\n" + + " \"path\": \"/accessToken/claims/aud/-\"\n" + + " }\n" + + " ]\n" + + "}\n"; + + // TODO: Handle status codes related to different scenarios + wireMockServer.stubFor(post(urlEqualTo(mockEndpoint)) + .withRequestBody(matchingJsonPath("$.event.request.grantType", equalTo("password"))) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(jsonResponse))); + + } catch (Exception e) { + throw new Exception("Error occurred while creating the mock server: " + e); + } + } + + /** + * Shut down the wiremock server instance. + */ + public static void shutDownMockServer() { + + wireMockServer.stop(); + } +} 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 425bec8c2d0..386b97836b2 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 @@ -54,6 +54,7 @@ import org.wso2.identity.integration.common.utils.ISIntegrationTest; import org.wso2.identity.integration.test.rest.api.server.api.resource.v1.model.APIResourceListItem; import org.wso2.identity.integration.test.rest.api.server.api.resource.v1.model.ScopeGetModel; +import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.AccessTokenConfiguration; import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.AdvancedApplicationConfiguration; 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; @@ -63,6 +64,7 @@ import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ClaimConfiguration; import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ClaimConfiguration.DialectEnum; import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ClaimMappings; +import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.DomainAPICreationModel; 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.application.management.v1.model.RequestedClaimConfiguration; @@ -105,7 +107,7 @@ public class OAuth2ServiceAbstractIntegrationTest extends ISIntegrationTest { public static final String OIDC = "oidc"; public static final String SAML = "saml"; private final static int TOMCAT_PORT = 8490; - + private static final boolean REQUIRES_AUTHORIZATION = true; protected ApplicationManagementServiceClient appMgtclient; protected OauthAdminClient adminClient; protected RemoteUserStoreManagerServiceClient remoteUSMServiceClient; @@ -173,6 +175,35 @@ public ApplicationResponseModel addApplication() throws Exception { return getApplication(appId); } + public ApplicationResponseModel addApplicationWithGrantType(String grantType) throws Exception { + + ApplicationModel application = new ApplicationModel(); + + List grantTypes = new ArrayList<>(); + Collections.addAll(grantTypes, grantType); + + List callBackUrls = new ArrayList<>(); + Collections.addAll(callBackUrls, OAuth2Constant.CALLBACK_URL); + + OpenIDConnectConfiguration oidcConfig = new OpenIDConnectConfiguration(); + oidcConfig.setGrantTypes(grantTypes); + oidcConfig.setCallbackURLs(callBackUrls); + AccessTokenConfiguration accessTokenConfig = new AccessTokenConfiguration().type("JWT"); + accessTokenConfig.setUserAccessTokenExpiryInSeconds(3600L); + accessTokenConfig.setApplicationAccessTokenExpiryInSeconds(3600L); + oidcConfig.setAccessToken(accessTokenConfig); + + InboundProtocols inboundProtocolsConfig = new InboundProtocols(); + inboundProtocolsConfig.setOidc(oidcConfig); + + application.setInboundProtocolConfiguration(inboundProtocolsConfig); + application.setName(SERVICE_PROVIDER_NAME); + application.setIsManagementApp(true); + String appId = addApplication(application); + + return getApplication(appId); + } + protected ClaimConfiguration setApplicationClaimConfig() { ClaimMappings emailClaim = new ClaimMappings().applicationClaim(EMAIL_CLAIM_URI); @@ -1078,4 +1109,90 @@ public String getRoleV2ResourceId(String roleName, String audienceType, String O } return null; } + + /** + * Create a domain API. + * + * @param domainAPICreationModel Domain API creation request model + * @return ID of the created Domain API + */ + public String createDomainAPI(DomainAPICreationModel domainAPICreationModel) { + + try { + return restClient.createDomainAPIResource(domainAPICreationModel); + } catch (Exception e) { + throw new RuntimeException("Error while creating domain API " + + domainAPICreationModel.getName()); + } + } + + /** + * Create a domain API with an external service integrated. + * + * @param externalServiceName Name of the external service to be integrated + * @param externalServiceURI URL of the external service + * @param domainScopes Custom scopes related to the domain API + * @return ID of the created domain API resource + */ + public String createDomainAPI(String externalServiceName, String externalServiceURI, + List domainScopes) { + + DomainAPICreationModel domainAPICreationModel = new DomainAPICreationModel(); + domainAPICreationModel.setName(externalServiceName); + domainAPICreationModel.setIdentifier(externalServiceURI); + domainAPICreationModel.setDescription("This is a test external service"); + domainAPICreationModel.setRequiresAuthorization(REQUIRES_AUTHORIZATION); + List newScopes = new ArrayList<>(); + domainScopes.forEach(scope -> { + ScopeGetModel newCustomScope = new ScopeGetModel(); + newCustomScope.setName(scope); + newCustomScope.setDescription("This is a test scope"); + newCustomScope.setDisplayName(scope); + newScopes.add(newCustomScope); + }); + domainAPICreationModel.setScopes(newScopes); + try { + return restClient.createDomainAPIResource(domainAPICreationModel); + } catch (Exception e) { + throw new RuntimeException("Error while creating domain API " + + domainAPICreationModel.getName()); + } + } + + /** + * Delete a domain API. + * + * @param domainAPIId ID of the domain API + * @return Status code of the domain API deletion + */ + public int deleteDomainAPI(String domainAPIId) { + + try { + return restClient.deleteDomainAPIResource(domainAPIId); + } catch (IOException e) { + throw new RuntimeException("Error while deleting domain API of Id " + domainAPIId); + } + } + + /** + * Authorize a domain API to an application. + * + * @param applicationId ID of the application + * @param domainAPIId ID of the domain API to be authorized + * @param domainScopes Custom scopes related to the domain API + */ + public void authorizeDomainAPIs(String applicationId, String domainAPIId, List domainScopes) { + + AuthorizedAPICreationModel authorizedDomainAPICreationModel = new AuthorizedAPICreationModel(); + authorizedDomainAPICreationModel.setId(domainAPIId); + authorizedDomainAPICreationModel.setPolicyIdentifier("RBAC"); + authorizedDomainAPICreationModel.setScopes(domainScopes); + try { + restClient.addAPIAuthorizationToApplication(applicationId, authorizedDomainAPICreationModel); + } catch (Exception e) { + throw new RuntimeException("Error while authorizing domain API " + + authorizedDomainAPICreationModel.getId() + + " to application " + applicationId, e); + } + } } diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/AuthorizedDomainAPIResponse.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/AuthorizedDomainAPIResponse.java new file mode 100644 index 00000000000..881d04d9c6c --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/AuthorizedDomainAPIResponse.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2024, 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.rest.api.server.application.management.v1.model; + +import org.wso2.identity.integration.test.rest.api.server.api.resource.v1.model.ScopeGetModel; + +import java.util.List; +import java.util.Objects; + +public class AuthorizedDomainAPIResponse { + + private String id; + private String name; + private String description; + private String identifier; + private String type; + private boolean requiresAuthorization; + private List scopes = null; + private List properties = null; + + public String getId() { + + return id; + } + + public void setId(String id) { + + this.id = id; + } + + public String getName() { + + return name; + } + + public void setName(String name) { + + this.name = name; + } + + public String getDescription() { + + return description; + } + + public void setDescription(String description) { + + this.description = description; + } + + public String getIdentifier() { + + return identifier; + } + + public void setIdentifier(String identifier) { + + this.identifier = identifier; + } + + public String getType() { + + return type; + } + + public void setType(String type) { + + this.type = type; + } + + public boolean isRequiresAuthorization() { + + return requiresAuthorization; + } + + public void setRequiresAuthorization(boolean requiresAuthorization) { + + this.requiresAuthorization = requiresAuthorization; + } + + public List getScopes() { + + return scopes; + } + + public void setScopes(List scopes) { + + this.scopes = scopes; + } + + public List getProperties() { + + return properties; + } + + public void setProperties(List properties) { + + this.properties = properties; + } + + @Override + public boolean equals(Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AuthorizedDomainAPIResponse that = (AuthorizedDomainAPIResponse) o; + return requiresAuthorization == that.requiresAuthorization && + Objects.equals(id, that.id) && + Objects.equals(name, that.name) && + Objects.equals(description, that.description) && + Objects.equals(identifier, that.identifier) && + Objects.equals(type, that.type) && + Objects.equals(scopes, that.scopes) && + Objects.equals(properties, that.properties); + } + + @Override + public int hashCode() { + + return Objects.hash(id, name, description, identifier, type, requiresAuthorization, scopes, properties); + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class AuthorizedDomainAPIResponse {\n"); + + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append(" identifier: ").append(toIndentedString(identifier)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" requiresAuthorization: ").append(toIndentedString(requiresAuthorization)).append("\n"); + sb.append(" scopes: ").append(toIndentedString(scopes)).append("\n"); + sb.append(" properties: ").append(toIndentedString(properties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + private String toIndentedString(Object o) { + + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/DomainAPICreationModel.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/DomainAPICreationModel.java new file mode 100644 index 00000000000..b6930aac267 --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/model/DomainAPICreationModel.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2024, 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.rest.api.server.application.management.v1.model; + +import org.wso2.identity.integration.test.rest.api.server.api.resource.v1.model.ScopeGetModel; + +import java.util.List; +import java.util.Objects; + +public class DomainAPICreationModel { + + private String name; + private String identifier; + private String description; + private boolean requiresAuthorization; + private List scopes = null; + + public String getName() { + + return name; + } + + public void setName(String name) { + + this.name = name; + } + + public String getIdentifier() { + + return identifier; + } + + public void setIdentifier(String identifier) { + + this.identifier = identifier; + } + + public String getDescription() { + + return description; + } + + public void setDescription(String description) { + + this.description = description; + } + + public Boolean getRequiresAuthorization() { + + return requiresAuthorization; + } + + public void setRequiresAuthorization(Boolean requiresAuthorization) { + + this.requiresAuthorization = requiresAuthorization; + } + + public List getScopes() { + + return scopes; + } + + public void setScopes(List scopes) { + + this.scopes = scopes; + } + + @Override + public boolean equals(Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DomainAPICreationModel domainAPICreationModel = (DomainAPICreationModel) o; + return Objects.equals(this.name, domainAPICreationModel.name) && + Objects.equals(this.description, domainAPICreationModel.description) && + Objects.equals(this.identifier, domainAPICreationModel.identifier) && + Objects.equals(this.requiresAuthorization, domainAPICreationModel.requiresAuthorization) && + Objects.equals(this.scopes, domainAPICreationModel.scopes); + } + + @Override + public int hashCode() { + + return Objects.hash(name, description, identifier, requiresAuthorization, scopes); + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class DomainAPICreationModel {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append(" identifier: ").append(toIndentedString(identifier)).append("\n"); + sb.append(" requiresAuthorization: ").append(toIndentedString(requiresAuthorization)).append("\n"); + sb.append(" scopes: ").append(toIndentedString(scopes)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/ActionsRestClient.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/ActionsRestClient.java new file mode 100644 index 00000000000..e2c47b0e7d1 --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/ActionsRestClient.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2024, 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.restclients; + +import io.restassured.http.ContentType; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang.StringUtils; +import org.apache.http.Header; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.message.BasicHeader; +import org.wso2.carbon.automation.engine.context.beans.Tenant; +import org.wso2.carbon.utils.multitenancy.MultitenantConstants; +import org.wso2.identity.integration.test.rest.api.server.action.management.v1.model.ActionModel; +import org.wso2.identity.integration.test.utils.OAuth2Constant; + +import java.io.IOException; + +/** + * Rest client which provides methods to interact with the Actions REST API. + * This client is responsible for managing actions of various types. + */ +public class ActionsRestClient extends RestBaseClient { + + private static final String PRE_ISSUE_ACCESS_TOKEN_TYPE = "preIssueAccessToken"; + private static final String ACTIONS_PATH = "/actions"; + private static final String PRE_ISSUE_ACCESS_TOKEN_PATH = "/preIssueAccessToken"; + private final String serverUrl; + private final String tenantDomain; + private final String username; + private final String password; + private final String actionsBasePath; + + public ActionsRestClient(String serverUrl, Tenant tenantInfo) { + + this.serverUrl = serverUrl; + this.tenantDomain = tenantInfo.getContextUser().getUserDomain(); + this.username = tenantInfo.getContextUser().getUserName(); + this.password = tenantInfo.getContextUser().getPassword(); + + actionsBasePath = getActionsPath(serverUrl, tenantDomain); + } + + /** + * Create an action of the specified type. + * + * @param actionModel Request object to create the action + * @param actionType Type of the action + * @return Status code of the action creation + * @throws IOException If an error occurred while creating the action + */ + public String createActionType(ActionModel actionModel, String actionType) throws IOException { + + String jsonRequestBody = toJSONString(actionModel); + + String endPointUrl; + endPointUrl = getActionEndpointOfType(actionType); + + try (CloseableHttpResponse response = getResponseOfHttpPost(endPointUrl, jsonRequestBody, getHeaders())) { + String[] locationElements = response.getHeaders(LOCATION_HEADER)[0].toString().split(PATH_SEPARATOR); + return locationElements[locationElements.length - 1]; + } + } + + /** + * Delete an action of the specified type by the provided ID. + * + * @param actionType Type of action + * @param actionId ID of the action + * @return Status code of the action deletion + * @throws IOException If an error occurred while deleting the action + */ + public int deleteActionType(String actionType, String actionId) throws IOException { + + String endPointUrl; + endPointUrl = getActionEndpointOfType(actionType) + "/" + actionId; + + try (CloseableHttpResponse response = getResponseOfHttpDelete(endPointUrl, getHeaders())) { + return response.getStatusLine().getStatusCode(); + } + } + + /** + * Retrieve the action endpoint according to the action type. + * + * @param actionType Type of action + * @return Action endpoint + */ + private String getActionEndpointOfType(String actionType) { + + switch (actionType) { + case PRE_ISSUE_ACCESS_TOKEN_TYPE: + return actionsBasePath + PRE_ISSUE_ACCESS_TOKEN_PATH; + default: + return StringUtils.EMPTY; + } + } + + /** + * Get path of the action endpoint. + * + * @param serverUrl Server URL + * @param tenantDomain Tenant Domain + * @return Path of the action endpoint + */ + private String getActionsPath(String serverUrl, String tenantDomain) { + + if (tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { + return serverUrl + API_SERVER_PATH + ACTIONS_PATH; + } else { + return serverUrl + TENANT_PATH + tenantDomain + PATH_SEPARATOR + API_SERVER_PATH + + ACTIONS_PATH; + } + } + + /** + * Retrieve headers. + * + * @return An array of headers + */ + private Header[] getHeaders() { + + Header[] headerList = new Header[3]; + headerList[0] = new BasicHeader(USER_AGENT_ATTRIBUTE, OAuth2Constant.USER_AGENT); + headerList[1] = new BasicHeader(AUTHORIZATION_ATTRIBUTE, BASIC_AUTHORIZATION_ATTRIBUTE + + Base64.encodeBase64String((username + ":" + password).getBytes()).trim()); + headerList[2] = new BasicHeader(CONTENT_TYPE_ATTRIBUTE, String.valueOf(ContentType.JSON)); + + return headerList; + } +} 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 c88855ca454..914083d0ca9 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 @@ -39,10 +39,12 @@ 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.DomainAPICreationModel; import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.ApplicationPatchModel; 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.ApplicationSharePOSTRequest; import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.AuthorizedAPICreationModel; +import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.AuthorizedDomainAPIResponse; import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.OpenIDConnectConfiguration; import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.SAML2ServiceProvider; import org.wso2.identity.integration.test.rest.api.server.roles.v2.model.RoleV2; @@ -52,6 +54,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -486,6 +489,39 @@ public List getAPIResourceScopes(String apiIdentifier) throws IOE } } + /** + * Creates a domain API. + * + * @param domainAPICreationModel Domain API create request model + * @return ID of the created domain API resource + */ + public String createDomainAPIResource(DomainAPICreationModel domainAPICreationModel) throws IOException { + + String jsonRequestBody = toJSONString(domainAPICreationModel); + + try (CloseableHttpResponse response = getResponseOfHttpPost(apiResourceManagementApiBasePath, jsonRequestBody, + getHeaders())) { + String[] locationElements = response.getHeaders(LOCATION_HEADER)[0].toString().split(PATH_SEPARATOR); + return locationElements[locationElements.length - 1]; + } + } + + /** + * Deletes a domain API. + * + * @param domainAPIId ID of the domain API to be deleted + * @return Status code of the action creation + * @throws IOException If an error occurred while deleting the domain API + */ + public int deleteDomainAPIResource(String domainAPIId) throws IOException { + + String endpointUrl = apiResourceManagementApiBasePath + "/" + domainAPIId; + + try (CloseableHttpResponse response = getResponseOfHttpDelete(endpointUrl, getHeaders())) { + return response.getStatusLine().getStatusCode(); + } + } + public List getRoles(String roleName, String audienceType, String audienceId) throws Exception { String endPointUrl = buildRoleSearchEndpoint(roleName, audienceType, audienceId); 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 6dd7f4df98e..fd48b6b5889 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 @@ -136,6 +136,7 @@ + diff --git a/pom.xml b/pom.xml index 4abe5fb379d..1e3faec6f9d 100755 --- a/pom.xml +++ b/pom.xml @@ -2059,6 +2059,18 @@ ${rest.assured.version} test + + org.wiremock + wiremock + ${wiremock.version} + test + + + org.yaml + snakeyaml + ${snakeyaml.version} + test + io.swagger swagger-annotations @@ -2561,6 +2573,8 @@ 6.13 2.0.0.AM26 2.0.0.AM4 + 3.9.1 + 2.2 5.0.0 1.5.22