diff --git a/modules/integration/tests-integration/tests-backend/pom.xml b/modules/integration/tests-integration/tests-backend/pom.xml index 08f1be8d61..20cdd8663f 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 0000000000..fdbed9d873 --- /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 0000000000..2f6cbce001 --- /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 0000000000..74fbbaf6e1 --- /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 425bec8c2d..386b97836b 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 0000000000..881d04d9c6 --- /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 0000000000..b6930aac26 --- /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 0000000000..e2c47b0e7d --- /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 c88855ca45..914083d0ca 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 6dd7f4df98..fd48b6b588 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 22ddcbc2b1..e82d296e5f 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