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 647202ccaa0..824c6bdc4c8 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 @@ -1219,6 +1219,43 @@ public void authorizeSystemAPIs(String applicationId, List apiIdentifier }); } + /** + * Authorize list of SYSTEM APIs to an application registered in sub organization. + * + * @param applicationId Application id. + * @param apiIdentifiers API identifiers to authorize. + * @throws Exception Error occured while authorizing APIs. + */ + public void authorizeSystemAPIsToSubOrganizationApp(String applicationId, List apiIdentifiers, + String switchedM2MToken) { + + apiIdentifiers.stream().forEach(apiIdentifier -> { + try { + List filteredAPIResource = + restClient.getAPIResourcesWithFilteringFromSubOrganization("identifier+eq+" + apiIdentifier, + switchedM2MToken); + if (filteredAPIResource == null || filteredAPIResource.isEmpty()) { + return; + } + String apiId = filteredAPIResource.get(0).getId(); + // Get API scopes. + List apiResourceScopes = restClient.getAPIResourceScopesInSubOrganization(apiId, + switchedM2MToken); + AuthorizedAPICreationModel authorizedAPICreationModel = new AuthorizedAPICreationModel(); + authorizedAPICreationModel.setId(apiId); + authorizedAPICreationModel.setPolicyIdentifier("RBAC"); + apiResourceScopes.forEach(scope -> { + authorizedAPICreationModel.addScopesItem(scope.getName()); + }); + restClient.addAPIAuthorizationToSubOrgApplication(applicationId, authorizedAPICreationModel, + switchedM2MToken); + } catch (Exception e) { + throw new RuntimeException("Error while authorizing system API " + apiIdentifier + " to application " + + applicationId, e); + } + }); + } + public String getRoleV2ResourceId(String roleName, String audienceType, String OrganizationId) throws Exception { List roles = restClient.getRoles(roleName, audienceType, OrganizationId); diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/OrganizationOAuth2ApplicationManagementSuccessTest.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/OrganizationOAuth2ApplicationManagementSuccessTest.java new file mode 100644 index 00000000000..ea240bb7e0c --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/application/management/v1/OrganizationOAuth2ApplicationManagementSuccessTest.java @@ -0,0 +1,198 @@ +package org.wso2.identity.integration.test.rest.api.server.application.management.v1; + +import com.nimbusds.oauth2.sdk.AccessTokenResponse; +import com.nimbusds.oauth2.sdk.AuthorizationGrant; +import com.nimbusds.oauth2.sdk.ClientCredentialsGrant; +import com.nimbusds.oauth2.sdk.ResourceOwnerPasswordCredentialsGrant; +import com.nimbusds.oauth2.sdk.Scope; +import com.nimbusds.oauth2.sdk.TokenRequest; +import com.nimbusds.oauth2.sdk.TokenResponse; +import com.nimbusds.oauth2.sdk.auth.ClientAuthentication; +import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic; +import com.nimbusds.oauth2.sdk.auth.Secret; +import com.nimbusds.oauth2.sdk.http.HTTPResponse; +import com.nimbusds.oauth2.sdk.id.ClientID; +import org.json.JSONObject; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; +import org.wso2.carbon.automation.engine.context.TestUserMode; +import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.*; +import org.wso2.identity.integration.test.rest.api.server.organization.management.v1.OrganizationManagementBaseTest; +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.restclients.OAuth2RestClient; +import org.wso2.identity.integration.test.restclients.OrgMgtRestClient; +import org.wso2.identity.integration.test.utils.OAuth2Constant; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class OrganizationOAuth2ApplicationManagementSuccessTest extends OrganizationManagementBaseTest { + + private static final String AUTHORIZED_APIS_JSON = "org-based-authorized-apis.json"; + private static final String SUB_ORG_NAME = "subOrg"; + + private OrgMgtRestClient orgMgtRestClient; + private OAuth2RestClient oAuth2RestClient; + private String subOrgId; + private String switchedM2MToken; + private String subOrgAppTokenCCGrant; + private String subOrganizationAppId; + + @Factory(dataProvider = "restAPIUserConfigProvider") + public OrganizationOAuth2ApplicationManagementSuccessTest(TestUserMode userMode) throws Exception { + + super.init(userMode); + this.context = isServer; + this.authenticatingUserName = context.getContextTenant().getTenantAdmin().getUserName(); + this.authenticatingCredential = context.getContextTenant().getTenantAdmin().getPassword(); + this.tenant = context.getContextTenant().getDomain(); + } + + @BeforeClass(alwaysRun = true) + public void initClass() throws Exception { + + super.testInit("v1", swaggerDefinition, tenant); + oAuth2RestClient = new OAuth2RestClient(serverURL, tenantInfo); + + orgMgtRestClient = new OrgMgtRestClient(isServer, tenantInfo, serverURL, + new JSONObject(readResource(AUTHORIZED_APIS_JSON, this.getClass()))); + subOrgId = orgMgtRestClient.addOrganization(SUB_ORG_NAME); + switchedM2MToken = orgMgtRestClient.switchM2MToken(subOrgId); + orgMgtRestClient.addOrganizationUser("sub-org-user", "SubOrgUser@123"); + } + + @AfterClass(alwaysRun = true) + public void atEnd() throws Exception { + + orgMgtRestClient.deleteOrganization(subOrgId); + orgMgtRestClient.closeHttpClient(); + oAuth2RestClient.closeHttpClient(); + } + + @Test + public void testCreateOAuth2ApplicationInOrganization() throws Exception { + + String body = readResource("create-basic-oauth2-application.json", this.getClass()); + + oAuth2RestClient.createApplicationInSubOrganization(body, switchedM2MToken); + subOrganizationAppId = oAuth2RestClient.getAppIdUsingAppNameInOrganization("My SAMPLE APP", + switchedM2MToken); + + // Authorizing the APIs to the sub organization application + authorizeSystemAPIsToSubOrganizationApp(oAuth2RestClient, subOrganizationAppId, + new ArrayList<>(Arrays.asList("/o/scim2/Roles", "/o/oauth2/introspect")), switchedM2MToken); + + ApplicationResponseModel applicationResponseModel = oAuth2RestClient.getSubOrgApplication( + subOrganizationAppId, switchedM2MToken); + Assert.assertNotNull(applicationResponseModel, "Application Response Model cannot be null"); + + Assert.assertEquals(applicationResponseModel.getName(), "My SAMPLE APP"); + + List authorizedAPIResponseList = oAuth2RestClient + .getAPIAuthorizationsFromOrganizationApplication(subOrganizationAppId, switchedM2MToken); + + Assert.assertNotNull(authorizedAPIResponseList, "Authorized API Response list cannot be null"); + Assert.assertEquals(authorizedAPIResponseList.size(), 2); + Assert.assertTrue(authorizedAPIResponseList.toString().contains("/o/scim2/Roles")); + Assert.assertEquals(authorizedAPIResponseList.get(1).getType(), "ORGANIZATION"); + } + + @Test + public void testCreateRoleCreationInOrganization() throws Exception { + + // Creating an application role for the sub organization application + RoleV2 role; + String displayName; + List schemas = Collections.emptyList(); + List permissions = new ArrayList<>(); + permissions.add(new Permission("internal_org_role_mgt_create")); + permissions.add(new Permission("internal_org_role_mgt_view")); + displayName = "My SAMPLE APP - User Manager Role"; + Audience roleAudience = new Audience("APPLICATION", subOrganizationAppId); + role = new RoleV2(roleAudience, displayName, permissions, schemas); + String roleID = oAuth2RestClient.createV2RolesInSubOrganization(role, switchedM2MToken); + + org.json.simple.JSONObject roleJsonObject = oAuth2RestClient.getSubOrgSCIM2RoleV2ByID(roleID, switchedM2MToken); + Assert.assertEquals(roleJsonObject.get("displayName").toString(), "My SAMPLE APP - User Manager Role", + "Role Response Model cannot be null"); + + String permissionString = roleJsonObject.get("permissions").toString(); + Assert.assertTrue(permissionString.contains("internal_org_role_mgt_create"), + "Permission array cannot be null"); + } + + @Test(dependsOnMethods = "testCreateOAuth2ApplicationInOrganization") + public void testIssueAccessTokenFromSubOrgApplicationFromCCGrant() throws Exception { + + String subOrganizationAppId = oAuth2RestClient.getAppIdUsingAppNameInOrganization("My SAMPLE APP", + switchedM2MToken); + OpenIDConnectConfiguration oidcConfig = oAuth2RestClient.getOIDCInboundDetailsForSubOrgApplications( + subOrganizationAppId, switchedM2MToken); + String subOrgAppClientId = oidcConfig.getClientId(); + String clientSecret = oidcConfig.getClientSecret(); + + // Issue access token from sub organization application + AccessTokenResponse accessTokenResponse = getSubOrgApplicationToken("client_credentials", subOrgAppClientId, clientSecret, subOrgId); + subOrgAppTokenCCGrant = accessTokenResponse.getTokens().getAccessToken().getValue(); + Assert.assertNotNull(subOrgAppTokenCCGrant); + String scopes = accessTokenResponse.getTokens().getAccessToken().getScope().toString(); + String[] scopeArray = scopes.split(" "); + Assert.assertTrue(Arrays.asList(scopeArray).contains("internal_org_role_mgt_create")); + Assert.assertTrue(Arrays.asList(scopeArray).contains("internal_org_role_mgt_view")); + Assert.assertTrue(Arrays.asList(scopeArray).contains("internal_org_role_mgt_update")); + Assert.assertTrue(Arrays.asList(scopeArray).contains("internal_org_role_mgt_delete")); + } + + @Test(dependsOnMethods = "testCreateRoleCreationInOrganization") + public void testResourceAccessFromTokensIssuedFromSubOrgApplication() throws Exception { + + // Access resources from tokens issued from sub organization application + org.json.simple.JSONObject v2RolesObject = oAuth2RestClient.getSubOrgSCIM2RoleV2(subOrgAppTokenCCGrant); + Assert.assertTrue(v2RolesObject.get("Resources").toString().contains("My SAMPLE APP - User Manager Role")); + } + + private AccessTokenResponse getSubOrgApplicationToken(String grantType, String clientId, String clientSecretStr, + String orgId) throws Exception { + + URI tokenEndpoint = new URI("https://localhost:9853/t/carbon.super/o/" + orgId + "/oauth2/token"); + + ClientID clientID = new ClientID(clientId); + Secret clientSecret = new Secret(clientSecretStr); + ClientAuthentication clientAuth = new ClientSecretBasic(clientID, clientSecret); + + AuthorizationGrant authorizationGrant; + switch (grantType) { + case OAuth2Constant.OAUTH2_GRANT_TYPE_CLIENT_CREDENTIALS: + authorizationGrant = new ClientCredentialsGrant(); + break; + case OAuth2Constant.OAUTH2_GRANT_TYPE_RESOURCE_OWNER: + authorizationGrant = new ResourceOwnerPasswordCredentialsGrant(null, null); + break; + default: + throw new Exception("Unsupported grant type"); + } + Scope scope = new Scope("SYSTEM"); + + TokenRequest request = new TokenRequest(tokenEndpoint, clientAuth, authorizationGrant, scope); + HTTPResponse tokenHTTPResp = request.toHTTPRequest().send(); + TokenResponse tokenResponse = TokenResponse.parse(tokenHTTPResp); + return tokenResponse.toSuccessResponse(); + } + + @DataProvider(name = "restAPIUserConfigProvider") + public static Object[][] restAPIUserConfigProvider() { + + return new Object[][]{ + {TestUserMode.SUPER_TENANT_ADMIN} + }; + } +} diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/organization/management/v1/OrganizationManagementBaseTest.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/organization/management/v1/OrganizationManagementBaseTest.java index 98428efdb44..22c6016ec05 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/organization/management/v1/OrganizationManagementBaseTest.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/server/organization/management/v1/OrganizationManagementBaseTest.java @@ -30,9 +30,12 @@ import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; +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.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.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.InboundProtocols; import org.wso2.identity.integration.test.rest.api.server.application.management.v1.model.OpenIDConnectConfiguration; import org.wso2.identity.integration.test.rest.api.server.common.RESTAPIServerTestBase; @@ -212,12 +215,26 @@ protected String getAppClientId(String applicationId) throws Exception { return oidcConfig.getClientId(); } + protected String getSubOrgAppClientId(String applicationId, String switchedToken) throws Exception { + + OpenIDConnectConfiguration oidcConfig = oAuth2RestClient.getOIDCInboundDetailsForSubOrgApplications( + applicationId, switchedToken); + return oidcConfig.getClientId(); + } + protected String getAppClientSecret(String applicationId) throws Exception { OpenIDConnectConfiguration oidcConfig = oAuth2RestClient.getOIDCInboundDetails(applicationId); return oidcConfig.getClientSecret(); } + protected String getSubOrgAppClientSecret(String applicationId, String switchedM2MToken) throws Exception { + + OpenIDConnectConfiguration oidcConfig = oAuth2RestClient.getOIDCInboundDetailsForSubOrgApplications( + applicationId, switchedM2MToken); + return oidcConfig.getClientSecret(); + } + protected String buildGetRequestURL(String endpointURL, String tenantDomain, List queryParams) { String authorizeEndpoint = getTenantQualifiedURL(endpointURL, tenantDomain); @@ -298,4 +315,41 @@ protected String createB2BUser(String switchedM2MToken) throws Exception { Assert.assertNotNull(b2bUserID, "B2B user creation failed."); return b2bUserID; } + + /** + * Authorize list of SYSTEM APIs to an application registered in sub organization. + * + * @param applicationId Application id. + * @param apiIdentifiers API identifiers to authorize. + * @throws Exception Error occured while authorizing APIs. + */ + public void authorizeSystemAPIsToSubOrganizationApp(OAuth2RestClient restClient, String applicationId, List apiIdentifiers, + String switchedM2MToken) { + + apiIdentifiers.stream().forEach(apiIdentifier -> { + try { + List filteredAPIResource = + restClient.getAPIResourcesWithFilteringFromSubOrganization("identifier+eq+" + apiIdentifier, + switchedM2MToken); + if (filteredAPIResource == null || filteredAPIResource.isEmpty()) { + return; + } + String apiId = filteredAPIResource.get(0).getId(); + // Get API scopes. + List apiResourceScopes = restClient.getAPIResourceScopesInSubOrganization(apiId, + switchedM2MToken); + AuthorizedAPICreationModel authorizedAPICreationModel = new AuthorizedAPICreationModel(); + authorizedAPICreationModel.setId(apiId); + authorizedAPICreationModel.setPolicyIdentifier("RBAC"); + apiResourceScopes.forEach(scope -> { + authorizedAPICreationModel.addScopesItem(scope.getName()); + }); + restClient.addAPIAuthorizationToSubOrgApplication(applicationId, authorizedAPICreationModel, + switchedM2MToken); + } catch (Exception e) { + throw new RuntimeException("Error while authorizing system API " + apiIdentifier + " to application " + + applicationId, e); + } + }); + } } 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 673b26d1729..05fc9f1d7ed 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 @@ -117,7 +117,9 @@ public class OAuth2RestClient extends RestBaseClient { private final String applicationManagementApiBasePath; private final String subOrgApplicationManagementApiBasePath; private final String apiResourceManagementApiBasePath; + private final String subOrgApiResourceManagementApiBasePath; private final String roleV2ApiBasePath; + private final String subOrgRoleV2ApiBasePath; private final String username; private final String password; private final String authorizeEndpoint; @@ -134,11 +136,13 @@ public OAuth2RestClient(String backendUrl, Tenant tenantInfo) throws IOException applicationManagementApiBasePath = getApplicationsPath(backendUrl, tenantDomain); subOrgApplicationManagementApiBasePath = getSubOrgApplicationsPath(backendUrl, tenantDomain); apiResourceManagementApiBasePath = getAPIResourcesPath(backendUrl, tenantDomain); + subOrgApiResourceManagementApiBasePath = getSubOrgAPIResourcesPath(backendUrl, tenantDomain); roleV2ApiBasePath = getSCIM2RoleV2Path(backendUrl, tenantDomain); authorizeEndpoint = getAuthorizeEndpoint(backendUrl, tenantDomain); commonAuthURL = getCommonAuthURL(backendUrl, tenantDomain); subOrgCommonAuthURL = getSubOrgCommonAuthURL(backendUrl); tokenEndpoint = getTokenEndpoint(backendUrl, tenantDomain); + subOrgRoleV2ApiBasePath = getOrganizationSCIM2RoleV2Path(backendUrl, tenantDomain); } /** @@ -165,6 +169,27 @@ public String createApplication(ApplicationModel application) throws IOException } } + /** + * Create an Application in sub organization. + * + * @param jsonRequest Application Model with application creation details. + * @return Id of the created application. + * @throws IOException If an error occurred while creating an application. + */ + public String createApplicationInSubOrganization(String jsonRequest, String switchedM2MToken) throws IOException { + + try (CloseableHttpResponse response = getResponseOfHttpPost(subOrgApplicationManagementApiBasePath, jsonRequest, + getHeadersWithBearerToken(switchedM2MToken))) { + + if (response.getStatusLine().getStatusCode() >= 400) { + String responseBody = EntityUtils.toString(response.getEntity()); + throw new RuntimeException("Error occurred while creating the application. Response: " + responseBody); + } + String[] locationElements = response.getHeaders(LOCATION_HEADER)[0].toString().split(PATH_SEPARATOR); + return locationElements[locationElements.length - 1]; + } + } + /** * To create V2 roles. * @@ -182,6 +207,27 @@ public String createV2Roles(RoleV2 role) throws IOException { } } + /** + * To create V2 roles in sub organization. + * + * @param role an instance of RoleV2 + * @return the roleID + * @throws IOException throws if an error occurs while creating the role. + */ + public String createV2RolesInSubOrganization(RoleV2 role, String switchedM2MToken) throws IOException { + + String jsonRequest = toJSONString(role); + try (CloseableHttpResponse response = getResponseOfHttpPost(subOrgRoleV2ApiBasePath, jsonRequest, + getHeadersWithBearerToken(switchedM2MToken))) { + if (response.getStatusLine().getStatusCode() >= 400) { + String responseBody = EntityUtils.toString(response.getEntity()); + throw new RuntimeException("Error occurred while creating the role. Response: " + responseBody); + } + String[] locationElements = response.getHeaders(LOCATION_HEADER)[0].toString().split(PATH_SEPARATOR); + return locationElements[locationElements.length - 1]; + } + } + /** * To delete V2 roles. * @@ -232,6 +278,26 @@ public ApplicationResponseModel getApplication(String appId) throws IOException } } + /** + * Get Application details. + * + * @param appId Application id. + * @return ApplicationResponseModel object. + * @throws IOException If an error occurred while getting an application. + */ + public ApplicationResponseModel getSubOrgApplication(String appId, String switchedToken) throws IOException { + + String endPointUrl = subOrgApplicationManagementApiBasePath + PATH_SEPARATOR + appId; + + try (CloseableHttpResponse response = getResponseOfHttpGet(endPointUrl, getHeadersWithBearerToken( + switchedToken))) { + String responseBody = EntityUtils.toString(response.getEntity()); + + ObjectMapper jsonWriter = new ObjectMapper(new JsonFactory()); + return jsonWriter.readValue(responseBody, ApplicationResponseModel.class); + } + } + /** * Get Application details by client id. * @@ -421,6 +487,13 @@ public OpenIDConnectConfiguration getOIDCInboundDetails(String appId) throws Exc return jsonWriter.readValue(responseBody, OpenIDConnectConfiguration.class); } + public OpenIDConnectConfiguration getOIDCInboundDetailsForSubOrgApplications(String appId, String switchedToken) throws Exception { + + String responseBody = getInboundProtocolConfigForSubOrgApplication(appId, OIDC, switchedToken); + ObjectMapper jsonWriter = new ObjectMapper(new JsonFactory()); + return jsonWriter.readValue(responseBody, OpenIDConnectConfiguration.class); + } + /** * Get SAML inbound configuration details of an application. * @@ -446,6 +519,16 @@ private String getConfig(String appId, String inboundType) throws Exception { } } + private String getInboundProtocolConfigForSubOrgApplication(String appId, String inboundType, String switchedToken) throws Exception { + + String endPointUrl = subOrgApplicationManagementApiBasePath + PATH_SEPARATOR + appId + INBOUND_PROTOCOLS_BASE_PATH + + PATH_SEPARATOR + inboundType; + + try (CloseableHttpResponse response = getResponseOfHttpGet(endPointUrl, getHeadersWithBearerToken(switchedToken))) { + return EntityUtils.toString(response.getEntity()); + } + } + /** * Update inbound configuration details of an application. * @@ -785,6 +868,15 @@ private String getAPIResourcesPath(String serverUrl, String tenantDomain) { } } + private String getSubOrgAPIResourcesPath(String serverUrl, String tenantDomain) { + + if (tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { + return serverUrl + ORGANIZATION_PATH + API_SERVER_BASE_PATH + API_RESOURCE_MANAGEMENT_PATH; + } + return serverUrl + TENANT_PATH + tenantDomain + PATH_SEPARATOR + ORGANIZATION_PATH + API_SERVER_BASE_PATH + + API_RESOURCE_MANAGEMENT_PATH; + } + private String getSCIM2RoleV2Path(String serverUrl, String tenantDomain) { if (tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { @@ -794,6 +886,15 @@ private String getSCIM2RoleV2Path(String serverUrl, String tenantDomain) { } } + private String getOrganizationSCIM2RoleV2Path(String serverUrl, String tenantDomain) { + + if (tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { + return serverUrl + ORGANIZATION_PATH + SCIM_BASE_PATH + ROLE_V2_BASE_PATH; + } else { + return serverUrl + TENANT_PATH + tenantDomain + PATH_SEPARATOR + ORGANIZATION_PATH + SCIM_BASE_PATH + ROLE_V2_BASE_PATH; + } + } + /** * Get the authorize endpoint. * @@ -894,6 +995,60 @@ public int addAPIAuthorizationToApplication(String appId, AuthorizedAPICreationM } } + /** + * Add API authorization to an application registered in a sub organization. + * + * @param appId Sub organization application id. + * @param authorizedAPICreationModel AuthorizedAPICreationModel object with api authorization details. + * @return Status code of the response. + * @throws IOException Error when getting the response. + */ + public int addAPIAuthorizationToSubOrgApplication(String appId, AuthorizedAPICreationModel + authorizedAPICreationModel, String switchedM2MToken) throws IOException { + + String jsonRequestBody = toJSONString(authorizedAPICreationModel); + String endPointUrl = subOrgApplicationManagementApiBasePath + PATH_SEPARATOR + appId + AUTHORIZED_API_BASE_PATH; + + try (CloseableHttpResponse response = getResponseOfHttpPost(endPointUrl, jsonRequestBody, + getHeadersWithBearerToken(switchedM2MToken))) { + return response.getStatusLine().getStatusCode(); + } + } + + /** + * Get sub organization roles by ID. + * + * @param roleId ID of the role. + * @param switchedAccessToken Authorized token to get the role for the provided ID. + * @return Role object of the provided role ID. + * @throws Exception If an exception occurred during the retrieval process. + */ + public org.json.simple.JSONObject getSubOrgSCIM2RoleV2ByID(String roleId, String switchedAccessToken) + throws Exception { + + String endPointUrl = subOrgRoleV2ApiBasePath + PATH_SEPARATOR + roleId; + + try (CloseableHttpResponse response = getResponseOfHttpGet(endPointUrl, + getHeadersWithBearerToken(switchedAccessToken))) { + return getJSONObject(EntityUtils.toString(response.getEntity())); + } + } + + /** + * Get sub organization roles. + * + * @param switchedAccessToken Authorized token to get the roles of an organization. + * @return Roles of an organization. + * @throws Exception If an exception occurred during the retrieval process. + */ + public org.json.simple.JSONObject getSubOrgSCIM2RoleV2(String switchedAccessToken) throws Exception { + + try (CloseableHttpResponse response = getResponseOfHttpGet(subOrgRoleV2ApiBasePath, + getHeadersWithBearerToken(switchedAccessToken))) { + return getJSONObject(EntityUtils.toString(response.getEntity())); + } + } + /** * Get API resources by filtering. * @@ -913,6 +1068,28 @@ public List getAPIResourcesWithFiltering(String apiResource } } + /** + * Get API resources by filtering from sub organization. + * + * @param apiResourceFilter API resource filter. + * @return List of API resources. + * @throws IOException Error when getting the filtered API resource. + */ + public List getAPIResourcesWithFilteringFromSubOrganization(String apiResourceFilter, + String switchedM2MToken) + throws IOException { + + String endPointUrl = subOrgApiResourceManagementApiBasePath + "?filter=" + apiResourceFilter; + try (CloseableHttpResponse response = getResponseOfHttpGet(endPointUrl, getHeadersWithBearerToken( + switchedM2MToken))) { + String responseBody = EntityUtils.toString(response.getEntity()); + ObjectMapper jsonWriter = new ObjectMapper(new JsonFactory()); + APIResourceListResponse apiResourceListResponse = + jsonWriter.readValue(responseBody, APIResourceListResponse.class); + return apiResourceListResponse.getApiResources(); + } + } + /** * Get API resource scopes. * @@ -931,6 +1108,26 @@ public List getAPIResourceScopes(String apiIdentifier) throws IOE } } + /** + * Get API resource scopes in sub organization. + * + * @param apiIdentifier API identifier. + * @return List of API resource scopes. + * @throws IOException Error when getting the scopes. + */ + public List getAPIResourceScopesInSubOrganization(String apiIdentifier, String switchedM2MToken) + throws IOException { + + String endPointUrl = subOrgApiResourceManagementApiBasePath + PATH_SEPARATOR + apiIdentifier; + try (CloseableHttpResponse response = getResponseOfHttpGet(endPointUrl, getHeadersWithBearerToken( + switchedM2MToken))) { + String responseBody = EntityUtils.toString(response.getEntity()); + ObjectMapper jsonWriter = new ObjectMapper(new JsonFactory()); + APIResourceResponse apiResourceResponse = jsonWriter.readValue(responseBody, APIResourceResponse.class); + return apiResourceResponse.getScopes(); + } + } + /** * Creates a domain API. * @@ -1152,6 +1349,33 @@ public List getAPIAuthorizationsFromApplication(String ap } } + /** + * Get API authorizations to an organization application. + * + * @param appId Application id. + * @param accessToken Authorized token to get the roles of an organization. + * @return Authorized API response. + * @throws IOException Error when getting the response. + */ + public List getAPIAuthorizationsFromOrganizationApplication(String appId, + String accessToken) throws IOException { + + final String endPointUrl = subOrgApplicationManagementApiBasePath + PATH_SEPARATOR + appId + AUTHORIZED_API_BASE_PATH; + + try (CloseableHttpResponse response = + getResponseOfHttpGet(endPointUrl, getHeadersWithBearerToken(accessToken))) { + + if (HttpServletResponse.SC_NOT_FOUND == response.getStatusLine().getStatusCode()) { + return null; + } + + String responseBody = EntityUtils.toString(response.getEntity()); + ObjectMapper jsonWriter = new ObjectMapper(new JsonFactory()); + return jsonWriter.readValue(responseBody, + jsonWriter.getTypeFactory().constructCollectionType(List.class, AuthorizedAPIResponse.class)); + } + } + /** * Update an existing authorized API resource. * diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java index b893a534aa5..e5a2aa7d7ac 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java @@ -46,6 +46,8 @@ import org.json.JSONException; import org.json.JSONObject; import org.json.simple.parser.JSONParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testng.Assert; import org.wso2.carbon.automation.engine.context.AutomationContext; import org.wso2.carbon.automation.engine.context.beans.Tenant; @@ -69,11 +71,13 @@ public class OrgMgtRestClient extends RestBaseClient { private static final String API_SERVER_BASE_PATH = "api/server/v1"; + private static final String SCIM2_BASE_PATH = "scim2"; private static final String APPLICATION_MANAGEMENT_PATH = "/applications"; private static final String ORGANIZATION_MANAGEMENT_PATH = "/organizations"; private static final String API_RESOURCE_MANAGEMENT_PATH = "/api-resources"; private static final String SELF_PATH = "/self"; private static final String AUTHORIZED_APIS_PATH = "/authorized-apis"; + private static final String SCIM2_USERS_PATH = "/Users"; private static final String B2B_APP_NAME = "b2b-app"; private static final String API_RESOURCES = "apiResources"; private static final String ID = "id"; @@ -88,6 +92,7 @@ public class OrgMgtRestClient extends RestBaseClient { private final String organizationManagementApiBasePath; private final String subOrganizationManagementApiBasePath; private final String apiResourceManagementApiBasePath; + private final String subOrgSCIM2UsersAPIBasePath; private final String authenticatingUserName; private final String authenticatingCredential; @@ -112,6 +117,7 @@ public OrgMgtRestClient(AutomationContext context, Tenant tenantInfo, String bas buildPath(baseUrl, tenantInfo.getDomain(), API_RESOURCE_MANAGEMENT_PATH); this.subOrganizationManagementApiBasePath = buildSubOrgPath(baseUrl, tenantInfo.getDomain(), ORGANIZATION_MANAGEMENT_PATH); + this.subOrgSCIM2UsersAPIBasePath = buildSubOrgSCIM2Path(baseUrl, tenantInfo.getDomain(), SCIM2_USERS_PATH); createB2BApplication(authorizedAPIs); } @@ -478,6 +484,15 @@ private String buildSubOrgPath(String serverUrl, String tenantDomain, String end endpointURL; } + private String buildSubOrgSCIM2Path(String serverUrl, String tenantDomain, String endpoint) { + +// if (tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { +// return serverUrl + ORGANIZATION_PATH + SCIM2_BASE_PATH + endpoint; +// } + return serverUrl + TENANT_PATH + tenantDomain + PATH_SEPARATOR + ORGANIZATION_PATH + SCIM2_BASE_PATH + + endpoint; + } + /** * Close the HTTP client. * @@ -489,4 +504,62 @@ public void closeHttpClient() throws IOException { oAuth2RestClient.closeHttpClient(); client.close(); } + + /** + * Add a user inside a sub organization. + * + * @param username Name of the organization. + * @return password of the created organization. + * @throws Exception If an error occurs while creating the organization. + */ + public String addOrganizationUser(String username, String password) throws Exception { + + String m2mToken = getM2MAccessToken(); + String body = buildOrgUserCreationRequestBody(username, password); + try (CloseableHttpResponse response = getResponseOfHttpPost(subOrgSCIM2UsersAPIBasePath, body, + getHeadersWithBearerToken(m2mToken))) { + if (response.getStatusLine().getStatusCode() >= 400) { + String responseBody = EntityUtils.toString(response.getEntity()); + throw new RuntimeException("Error occurred while creating the role. Response: " + responseBody); + } + String[] locationElements = response.getHeaders(LOCATION_HEADER)[0].toString().split(PATH_SEPARATOR); + return locationElements[locationElements.length - 1]; + } + } + + private String buildOrgUserCreationRequestBody(String username, String password) throws JSONException { + + JSONObject userPayload = new JSONObject(); + userPayload.put("schemas", new JSONArray()); + + JSONObject name = new JSONObject(); + name.put("givenName", "Kim05"); + name.put("familyName", "Berry05"); + userPayload.put("name", name); + + userPayload.put("userName", username); + userPayload.put("password", password); + + JSONArray emails = new JSONArray(); + JSONObject email1 = new JSONObject(); + email1.put("value", "kim05@gmail.com"); + emails.put(email1); + + JSONObject email2 = new JSONObject(); + email2.put("type", "work"); + email2.put("value", "kim05@wso2.com"); + emails.put(email2); + + userPayload.put("emails", emails); + + JSONObject enterpriseUser = new JSONObject(); + enterpriseUser.put("employeeNumber", "1234A03"); + + JSONObject manager = new JSONObject(); + manager.put("value", "Taylor"); + enterpriseUser.put("manager", manager); + + userPayload.put("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", enterpriseUser); + return userPayload.toString(); + } } diff --git a/modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/rest/api/server/application/management/v1/create-basic-oauth2-application.json b/modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/rest/api/server/application/management/v1/create-basic-oauth2-application.json new file mode 100644 index 00000000000..416de5d15c8 --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/rest/api/server/application/management/v1/create-basic-oauth2-application.json @@ -0,0 +1,20 @@ +{ + "name": "My SAMPLE APP", + "description": "my application 2", + "imageUrl": "https://localhost/image", + "accessUrl": "https://localhost/accessUrl", + "associatedRoles": { + "allowedAudience": "APPLICATION", + "roles": [] + }, + "inboundProtocolConfiguration": { + "oidc": { + "grantTypes": [ + "client_credentials", + "password", + "refresh_token" + ], + "isFAPIApplication": false + } + } +} diff --git a/modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/rest/api/server/application/management/v1/org-based-authorized-apis.json b/modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/rest/api/server/application/management/v1/org-based-authorized-apis.json new file mode 100644 index 00000000000..9b8637ff574 --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/rest/api/server/application/management/v1/org-based-authorized-apis.json @@ -0,0 +1,42 @@ +{ + "/api/server/v1/organizations": [ + "internal_organization_view", + "internal_organization_create", + "internal_organization_delete" + ], + "/o/api/server/v1/userstore": [ + "internal_org_userstore_create", + "internal_org_userstore_delete", + "internal_org_userstore_view" + ], + "/o/scim2/Users": [ + "internal_org_user_mgt_create", + "internal_org_user_mgt_view", + "internal_org_user_mgt_list", + "internal_org_user_mgt_delete" + ], + "/o/scim2/Groups": [ + "internal_org_group_mgt_create", + "internal_org_group_mgt_view", + "internal_org_group_mgt_delete" + ], + "/o/api/server/v1/claim-dialects": [ + "internal_org_claim_meta_view", + "internal_org_claim_meta_update" + ], + "/o/api/server/v1/applications": [ + "internal_org_application_mgt_create", + "internal_org_application_mgt_view", + "internal_org_application_mgt_update", + "internal_org_application_mgt_delete" + ], + "/o/api/server/v1/api-resources": [ + "internal_org_api_resource_view" + ], + "/o/scim2/Roles": [ + "internal_org_role_mgt_view", + "internal_org_role_mgt_create", + "internal_org_role_mgt_update", + "internal_org_role_mgt_delete" + ] +} 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 7bfcc0635c4..8e49c1e4840 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 @@ -245,6 +245,7 @@ +