Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Ancestor App IDs Retrieval and Child App IDs Retrieval Support for a Given Shared App #383

Merged
merged 15 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.role.v2.mgt.core</artifactId>
</dependency>
<dependency>
<groupId>commons-collections.wso2</groupId>
<artifactId>commons-collections</artifactId>
</dependency>
<!--Test Dependencies-->
<dependency>
<groupId>org.testng</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, WSO2 LLC. (http://www.wso2.com).
* Copyright (c) 2022-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
Expand All @@ -20,10 +20,12 @@

import org.wso2.carbon.identity.application.common.model.ServiceProvider;
import org.wso2.carbon.identity.organization.management.application.model.SharedApplication;
import org.wso2.carbon.identity.organization.management.service.exception.NotImplementedException;
import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
import org.wso2.carbon.identity.organization.management.service.model.BasicOrganization;

import java.util.List;
import java.util.Map;

/**
* Interface for Organization Application Management.
Expand All @@ -33,14 +35,14 @@ public interface OrgApplicationManager {
/**
* Share the application to all the child organizations or to a list of child organizations based on the user input.
*
* @param ownerOrgId Identifier of the organization owning the application.
* @param mainAppId Identifier of the main application.
* @param shareWithAllChildren Attribute indicating if the application is shared with all sub-organizations.
* @param sharedOrgs Optional list of identifiers of child organization to share the application.
* @param ownerOrgId Identifier of the organization owning the application.
* @param mainAppId Identifier of the main application.
* @param shareWithAllChildren Attribute indicating if the application is shared with all sub-organizations.
* @param sharedOrgs Optional list of identifiers of child organization to share the application.
* @throws OrganizationManagementException on errors when sharing the application.
*/
void shareOrganizationApplication(String ownerOrgId, String mainAppId, boolean shareWithAllChildren,
List<String> sharedOrgs) throws OrganizationManagementException;
List<String> sharedOrgs) throws OrganizationManagementException;

/**
* Remove the shared (fragment) application for given organization to stop sharing the business application.
Expand Down Expand Up @@ -68,7 +70,7 @@ List<BasicOrganization> getApplicationSharedOrganizations(String ownerOrgId, Str
* Returns the shared applications list of a given primary application, along with their organizations.
*
* @param ownerOrgId ID of the organization owning the primary application.
* @param mainAppId UUID of the primary application.
* @param mainAppId UUID of the primary application.
* @return A list of shared applications details.
* @throws OrganizationManagementException on errors occurred while retrieving the list of shared applications.
*/
Expand Down Expand Up @@ -143,5 +145,35 @@ default String getMainApplicationIdForGivenSharedApp(String sharedAppId, String
* @throws OrganizationManagementException on errors when sharing the application.
*/
void shareApplication(String ownerOrgId, String sharedOrgId, ServiceProvider mainApplication,
boolean shareWithAllChildren) throws OrganizationManagementException;
boolean shareWithAllChildren) throws OrganizationManagementException;

/**
* Get the shared ancestor application IDs for the given child application ID of the given child organization.
*
* @param childAppId The unique ID of the shared child application.
* @param childOrgId The organization ID of the child.
* @return Map containing shared ancestor application IDs and their organization IDs.
* @throws OrganizationManagementException If errors occurred when retrieving the shared ancestor application IDs
*/
default Map<String, String> getAncestorAppIds(String childAppId, String childOrgId)
throws OrganizationManagementException {

throw new NotImplementedException(
"getAncestorAppIds method is not implemented in " + this.getClass().getName());
}

/**
* Get shared child application IDs of the given parent application.
*
* @param parentAppId The unique ID of the root app / shared app in parent org.
* @param parentOrgId The organization ID of the parent.
* @param childOrgIds The organization ID list of the children.
* @return The map containing organization ID and application ID of the shared child applications.
* @throws OrganizationManagementException If errors occurred when retrieving the child app IDs.
*/
default Map<String, String> getChildAppIds(String parentAppId, String parentOrgId, List<String> childOrgIds)
throws OrganizationManagementException {

throw new NotImplementedException("getChildAppIds method is not implemented in " + this.getClass().getName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -655,6 +656,92 @@ public void shareApplication(String ownerOrgId, String sharedOrgId, ServiceProvi
}
}

@Override
public Map<String, String> getAncestorAppIds(String childAppId, String childOrgId)
throws OrganizationManagementException {

Optional<MainApplicationDO> mainApplicationDO =
getOrgApplicationMgtDAO().getMainApplication(childAppId, childOrgId);
if (!mainApplicationDO.isPresent()) {
// Check if the child app is a main application.
if (isMainApp(childAppId, childOrgId)) {
return Collections.singletonMap(childOrgId, childAppId);
}
return Collections.emptyMap();
}

String ownerOrgId = mainApplicationDO.get().getOrganizationId();
String mainAppId = mainApplicationDO.get().getMainApplicationId();
List<String> ancestorOrganizationIds = getOrganizationManager().getAncestorOrganizationIds(childOrgId);
Map<String, String> ancestorAppIds = new HashMap<>();
// Add main app to the map.
ancestorAppIds.put(ownerOrgId, mainAppId);
if (CollectionUtils.isNotEmpty(ancestorOrganizationIds) && ancestorOrganizationIds.size() > 1) {
List<SharedApplicationDO> ancestorApplications =
getOrgApplicationMgtDAO().getSharedApplications(mainAppId, ownerOrgId,
ancestorOrganizationIds.subList(0, ancestorOrganizationIds.size() - 1));
ancestorApplications.forEach(ancestorApplication -> ancestorAppIds.put(
ancestorApplication.getOrganizationId(), ancestorApplication.getFragmentApplicationId()));
}
return ancestorAppIds;
}

@Override
public Map<String, String> getChildAppIds(String parentAppId, String parentOrgId, List<String> childOrgIds)
throws OrganizationManagementException {

if (CollectionUtils.isEmpty(childOrgIds)) {
return Collections.emptyMap();
}

// Check if the parent application is a main application.
if (isMainApp(parentAppId, parentOrgId)) {
return getFilteredChildApplications(parentAppId, parentOrgId, childOrgIds);
}

Optional<MainApplicationDO> mainApplicationDO =
getOrgApplicationMgtDAO().getMainApplication(parentAppId, parentOrgId);
if (mainApplicationDO.isPresent()) {
return getFilteredChildApplications(mainApplicationDO.get().getMainApplicationId(),
mainApplicationDO.get().getOrganizationId(), childOrgIds);
}
return Collections.emptyMap();
}

/**
* Returns whether the given application is a main application.
*
* @param appId The unique ID of the application.
* @param orgId The organization ID of the given application.
* @return Returns true if the given application is a main application.
* @throws OrganizationManagementException If an error occurs while retrieving the application.
*/
private boolean isMainApp(String appId, String orgId) throws OrganizationManagementException {

OrganizationManager organizationManager = OrgApplicationMgtDataHolder.getInstance().getOrganizationManager();
String tenantDomain = organizationManager.resolveTenantDomain(orgId);

ApplicationManagementService applicationManagementService =
OrgApplicationMgtDataHolder.getInstance().getApplicationManagementService();
ServiceProvider serviceProvider;
try {
serviceProvider = applicationManagementService.getApplicationByResourceId(appId, tenantDomain);
} catch (IdentityApplicationManagementException e) {
throw handleServerException(ERROR_CODE_ERROR_RETRIEVING_APPLICATION, e, appId);
}

if (serviceProvider != null) {
boolean isFragmentApp = Arrays.stream(serviceProvider.getSpProperties())
.anyMatch(property -> IS_FRAGMENT_APP.equals(property.getName()) &&
Boolean.parseBoolean(property.getValue()));
if (!isFragmentApp) {
// Given app is a main application.
return true;
}
}
return false;
}

/**
* This method checks whether the exception is thrown due to the OAuth client already existing.
* @param e The IdentityException thrown upon OAuth app creation failure.
Expand Down Expand Up @@ -902,6 +989,24 @@ private boolean shouldUpdateShareWithAllChildren(boolean shareWithAllChildren, S
return false;
}

/**
* Filter and return shared child applications of the given main application for the given child organization IDs.
*
* @param mainAppId Application ID of the main application.
* @param mainOrgId Organization ID of the main application.
* @return The map containing organization ID and application ID of the filtered shared applications.
* @throws OrganizationManagementException If an error occurs while retrieving child applications.
*/
dhaura marked this conversation as resolved.
Show resolved Hide resolved
private Map<String, String> getFilteredChildApplications(String mainAppId, String mainOrgId,
List<String> childOrgIds)
throws OrganizationManagementException {

List<SharedApplicationDO> childApplications =
getOrgApplicationMgtDAO().getSharedApplications(mainAppId, mainOrgId, childOrgIds);
return childApplications.stream().collect(Collectors.toMap(
SharedApplicationDO::getOrganizationId, SharedApplicationDO::getFragmentApplicationId));
}

private OAuthAdminServiceImpl getOAuthAdminService() {

return OrgApplicationMgtDataHolder.getInstance().getOAuthAdminService();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, WSO2 LLC. (http://www.wso2.com).
* Copyright (c) 2022-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
Expand Down Expand Up @@ -67,6 +67,12 @@ public class SQLConstants {
SQLPlaceholders.DB_SCHEMA_COLUMN_NAME_MAIN_APP_ID + "; AND OWNER_ORG_ID = :"
+ SQLPlaceholders.DB_SCHEMA_COLUMN_NAME_OWNER_ORG_ID + ";";

public static final String GET_FILTERED_SHARED_APPLICATIONS =
"SELECT SHARED_ORG_ID, SHARED_APP_ID FROM SP_SHARED_APP WHERE MAIN_APP_ID = :" +
SQLPlaceholders.DB_SCHEMA_COLUMN_NAME_MAIN_APP_ID + "; AND OWNER_ORG_ID = :" +
SQLPlaceholders.DB_SCHEMA_COLUMN_NAME_OWNER_ORG_ID + "; AND SHARED_ORG_ID IN (" +
SQLPlaceholders.SHARED_ORG_ID_LIST_PLACEHOLDER + ")";

private SQLConstants() {

}
Expand All @@ -86,6 +92,9 @@ public static final class SQLPlaceholders {
public static final String DB_SCHEMA_COLUMN_NAME_METADATA_VALUE = "METADATA_VALUE";
public static final String DB_SCHEMA_COLUMN_NAME_SP_APP_ID = "SP_APP_ID";

public static final String SHARED_ORG_ID_LIST_PLACEHOLDER = "_SHARED_ORG_ID_LIST_";
public static final String SHARED_ORG_ID_PLACEHOLDER_PREFIX = "SHARED_ORG_ID_";

private SQLPlaceholders() {

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, WSO2 LLC. (http://www.wso2.com).
* Copyright (c) 2022-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
Expand All @@ -20,6 +20,7 @@

import org.wso2.carbon.identity.organization.management.application.model.MainApplicationDO;
import org.wso2.carbon.identity.organization.management.application.model.SharedApplicationDO;
import org.wso2.carbon.identity.organization.management.service.exception.NotImplementedException;
import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;

import java.util.List;
Expand Down Expand Up @@ -118,4 +119,23 @@ Optional<String> getSharedApplicationResourceId(String mainAppId, String ownerOr
*/
void updateShareWithAllChildren(String mainApplicationId, String ownerOrganizationId, boolean shareWithAllChildren)
throws OrganizationManagementException;

/**
* Returns the unique identifiers of shared applications associated with the given main application
* within the given shared organizations.
*
* @param mainAppId The app ID of the main application.
* @param ownerOrgId The organization ID of the owner.
* @param sharedOrgIds The list of app shared organization IDs.
* @return The list of shared application IDs within the given shared organizations.
* @throws OrganizationManagementException The server exception is thrown in a failure
dhaura marked this conversation as resolved.
Show resolved Hide resolved
* when retrieving the shared apps.
*/
default List<SharedApplicationDO> getSharedApplications(String mainAppId, String ownerOrgId,
List<String> sharedOrgIds)
throws OrganizationManagementException {

throw new NotImplementedException(
"getSharedApplications method is not implemented in " + this.getClass().getName());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, WSO2 LLC. (http://www.wso2.com).
* Copyright (c) 2022-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
Expand All @@ -18,6 +18,7 @@

package org.wso2.carbon.identity.organization.management.application.dao.impl;

import org.apache.commons.collections.CollectionUtils;
import org.wso2.carbon.database.utils.jdbc.NamedJdbcTemplate;
import org.wso2.carbon.database.utils.jdbc.exceptions.DataAccessException;
import org.wso2.carbon.database.utils.jdbc.exceptions.TransactionException;
Expand All @@ -28,10 +29,14 @@
import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementServerException;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static org.wso2.carbon.identity.organization.management.application.constant.OrgApplicationMgtConstants.IS_FRAGMENT_APP;
import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.GET_FILTERED_SHARED_APPLICATIONS;
import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.GET_MAIN_APPLICATION;
import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.GET_SHARED_APPLICATION;
import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.GET_SHARED_APPLICATIONS;
Expand All @@ -49,6 +54,8 @@
import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.SQLPlaceholders.DB_SCHEMA_COLUMN_NAME_SHARE_WITH_ALL_CHILDREN;
import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.SQLPlaceholders.DB_SCHEMA_COLUMN_NAME_SP_APP_ID;
import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.SQLPlaceholders.DB_SCHEMA_COLUMN_NAME_SP_ID;
import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.SQLPlaceholders.SHARED_ORG_ID_LIST_PLACEHOLDER;
import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.SQLPlaceholders.SHARED_ORG_ID_PLACEHOLDER_PREFIX;
import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.UPDATE_SHARE_WITH_ALL_CHILDREN;
import static org.wso2.carbon.identity.organization.management.application.util.OrgApplicationManagerUtil.getNewTemplate;
import static org.wso2.carbon.identity.organization.management.service.constant.OrganizationManagementConstants.ErrorMessages.ERROR_CODE_ERROR_CHECKING_APPLICATION_HAS_FRAGMENTS;
Expand Down Expand Up @@ -224,6 +231,39 @@ public void updateShareWithAllChildren(String mainApplicationId, String ownerOrg
}
}

@Override
public List<SharedApplicationDO> getSharedApplications(String mainAppId, String ownerOrgId,
List<String> sharedOrgIds)
throws OrganizationManagementException {

if (CollectionUtils.isEmpty(sharedOrgIds)) {
return Collections.emptyList();
}

String placeholders = IntStream.range(0, sharedOrgIds.size())
.mapToObj(i -> ":" + SHARED_ORG_ID_PLACEHOLDER_PREFIX + i + ";")
.collect(Collectors.joining(", "));
String sqlStmt = GET_FILTERED_SHARED_APPLICATIONS.replace(SHARED_ORG_ID_LIST_PLACEHOLDER, placeholders);

NamedJdbcTemplate namedJdbcTemplate = getNewTemplate();
try {
return namedJdbcTemplate.executeQuery(sqlStmt,
(resultSet, rowNumber) -> new SharedApplicationDO(
resultSet.getString(DB_SCHEMA_COLUMN_NAME_SHARED_ORG_ID),
resultSet.getString(DB_SCHEMA_COLUMN_NAME_SHARED_APP_ID)),
namedPreparedStatement -> {
namedPreparedStatement.setString(DB_SCHEMA_COLUMN_NAME_MAIN_APP_ID, mainAppId);
namedPreparedStatement.setString(DB_SCHEMA_COLUMN_NAME_OWNER_ORG_ID, ownerOrgId);
for (int i = 0; i < sharedOrgIds.size(); i++) {
namedPreparedStatement.setString(SHARED_ORG_ID_PLACEHOLDER_PREFIX + i, sharedOrgIds.get(i));
}
});
} catch (DataAccessException e) {
throw handleServerException(
ERROR_CODE_ERROR_RESOLVING_SHARED_APPLICATION, e, mainAppId, ownerOrgId);
}
}

/**
* Get the value for the shareWithAllChildren column according to the db type.
*
Expand Down
Loading
Loading