diff --git a/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/OrgApplicationManager.java b/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/OrgApplicationManager.java index d475623f..80dadcdd 100644 --- a/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/OrgApplicationManager.java +++ b/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/OrgApplicationManager.java @@ -18,6 +18,7 @@ package org.wso2.carbon.identity.organization.management.application; +import org.wso2.carbon.identity.application.common.model.ApplicationBasicInfo; 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; @@ -176,4 +177,39 @@ default Map getChildAppIds(String parentAppId, String parentOrgI throw new NotImplementedException("getChildAppIds method is not implemented in " + this.getClass().getName()); } + + /** + * Get the discoverable shared application basic info. + * + * @param limit Maximum no of applications to be returned in the result set (optional). + * @param offset Zero based index of the first application to be returned in the result set (optional). + * @param filter Filter to search for applications (optional). + * @param sortOrder Sort order, ascending or descending (optional). + * @param sortBy Attribute to sort from (optional). + * @param tenantDomain Tenant domain. + * @return List of DiscoverableApplicationBasicInfo of applications matching the given criteria. + * @throws OrganizationManagementException If an error occurred when retrieving the discoverable applications. + */ + default List getDiscoverableSharedApplicationBasicInfo(int limit, int offset, String filter, + String sortOrder, + String sortBy, String tenantDomain) + throws OrganizationManagementException { + + return null; + } + + /** + * Get the count of discoverable shared applications. + * + * @param filter Filter to search for applications (optional). + * @param tenantDomain Tenant domain. + * @return Count of discoverable applications matching given filter. + * @throws OrganizationManagementException If an error occurred when retrieving the count of + * discoverable applications. + */ + default int getCountOfDiscoverableSharedApplications(String filter, String tenantDomain) + throws OrganizationManagementException { + + return 0; + } } diff --git a/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/OrgApplicationManagerImpl.java b/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/OrgApplicationManagerImpl.java index 45bc8ec0..d6c325a5 100644 --- a/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/OrgApplicationManagerImpl.java +++ b/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/OrgApplicationManagerImpl.java @@ -26,6 +26,7 @@ import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException; +import org.wso2.carbon.identity.application.common.model.ApplicationBasicInfo; import org.wso2.carbon.identity.application.common.model.AuthenticationStep; import org.wso2.carbon.identity.application.common.model.FederatedAuthenticatorConfig; import org.wso2.carbon.identity.application.common.model.IdentityProvider; @@ -709,6 +710,25 @@ public Map getChildAppIds(String parentAppId, String parentOrgId return Collections.emptyMap(); } + @Override + public List getDiscoverableSharedApplicationBasicInfo(int limit, int offset, String filter, + String sortOrder, String sortBy, + String tenantDomain) + throws OrganizationManagementException { + + String rootOrgId = getOrganizationManager().getPrimaryOrganizationId(tenantDomain); + return getOrgApplicationMgtDAO().getDiscoverableSharedApplicationBasicInfo(limit, offset, filter, sortOrder, + sortBy, tenantDomain, rootOrgId); + } + + @Override + public int getCountOfDiscoverableSharedApplications(String filter, String tenantDomain) + throws OrganizationManagementException { + + String rootOrgId = getOrganizationManager().getPrimaryOrganizationId(tenantDomain); + return getOrgApplicationMgtDAO().getCountOfDiscoverableSharedApplications(filter, tenantDomain, rootOrgId); + } + /** * Returns whether the given application is a main application. * diff --git a/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/constant/SQLConstants.java b/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/constant/SQLConstants.java index f2f3fd2f..3d8f8cf4 100644 --- a/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/constant/SQLConstants.java +++ b/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/constant/SQLConstants.java @@ -73,6 +73,109 @@ public class SQLConstants { SQLPlaceholders.DB_SCHEMA_COLUMN_NAME_OWNER_ORG_ID + "; AND SHARED_ORG_ID IN (" + SQLPlaceholders.SHARED_ORG_ID_LIST_PLACEHOLDER + ")"; + public static final String LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_MYSQL = + "SELECT sa_shared.ID, sa_shared.APP_NAME, sa_shared.DESCRIPTION, sa_shared.UUID, sa_shared.IMAGE_URL, " + + "CASE WHEN sa_shared.ACCESS_URL IS NOT NULL THEN sa_shared.ACCESS_URL ELSE sa_main.ACCESS_URL END AS " + + "ACCESS_URL, sa_shared.USERNAME, sa_shared.USER_STORE, sa_shared.TENANT_ID FROM SP_SHARED_APP ssa " + + "JOIN SP_APP sa_main ON ssa.MAIN_APP_ID = sa_main.UUID " + + "JOIN SP_APP sa_shared ON ssa.SHARED_APP_ID = sa_shared.UUID WHERE ssa.SHARED_ORG_ID = ? AND " + + "ssa.OWNER_ORG_ID = ? AND (sa_main.IS_DISCOVERABLE = '1' OR sa_shared.IS_DISCOVERABLE = '1') " + + "ORDER BY ID DESC LIMIT ?, ?"; + + public static final String LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_POSTGRES = + "SELECT sa_shared.ID, sa_shared.APP_NAME, sa_shared.DESCRIPTION, sa_shared.UUID, sa_shared.IMAGE_URL, " + + "CASE WHEN sa_shared.ACCESS_URL IS NOT NULL THEN sa_shared.ACCESS_URL ELSE sa_main.ACCESS_URL END AS " + + "ACCESS_URL, sa_shared.USERNAME, sa_shared.USER_STORE, sa_shared.TENANT_ID FROM SP_SHARED_APP ssa " + + "JOIN SP_APP sa_main ON ssa.MAIN_APP_ID = sa_main.UUID " + + "JOIN SP_APP sa_shared ON ssa.SHARED_APP_ID = sa_shared.UUID WHERE ssa.SHARED_ORG_ID = ? AND " + + "ssa.OWNER_ORG_ID = ? AND (sa_main.IS_DISCOVERABLE = '1' OR sa_shared.IS_DISCOVERABLE = '1') " + + "ORDER BY ID DESC OFFSET ? LIMIT ?"; + + public static final String LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_ORACLE = + "SELECT sa_shared.ID, sa_shared.APP_NAME, sa_shared.DESCRIPTION, sa_shared.UUID, sa_shared.IMAGE_URL, " + + "CASE WHEN sa_shared.ACCESS_URL IS NOT NULL THEN sa_shared.ACCESS_URL ELSE sa_main.ACCESS_URL END AS " + + "ACCESS_URL, sa_shared.USERNAME, sa_shared.USER_STORE, sa_shared.TENANT_ID FROM SP_SHARED_APP ssa " + + "JOIN SP_APP sa_main ON ssa.MAIN_APP_ID = sa_main.UUID " + + "JOIN SP_APP sa_shared ON ssa.SHARED_APP_ID = sa_shared.UUID WHERE ssa.SHARED_ORG_ID = ? AND " + + "ssa.OWNER_ORG_ID = ? AND (sa_main.IS_DISCOVERABLE = '1' OR sa_shared.IS_DISCOVERABLE = '1') " + + "BETWEEN ? AND ? ORDER BY ID DESC"; + + public static final String LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_MSSQL = + "SELECT sa_shared.ID, sa_shared.APP_NAME, sa_shared.DESCRIPTION, sa_shared.UUID, sa_shared.IMAGE_URL, " + + "CASE WHEN sa_shared.ACCESS_URL IS NOT NULL THEN sa_shared.ACCESS_URL ELSE sa_main.ACCESS_URL END AS " + + "ACCESS_URL, sa_shared.USERNAME, sa_shared.USER_STORE, sa_shared.TENANT_ID FROM SP_SHARED_APP ssa " + + "JOIN SP_APP sa_main ON ssa.MAIN_APP_ID = sa_main.UUID " + + "JOIN SP_APP sa_shared ON ssa.SHARED_APP_ID = sa_shared.UUID WHERE ssa.SHARED_ORG_ID = ? AND " + + "ssa.OWNER_ORG_ID = ? AND (sa_main.IS_DISCOVERABLE = '1' OR sa_shared.IS_DISCOVERABLE = '1') " + + "ORDER BY ID DESC OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; + + public static final String LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_INFORMIX = + "SELECT sa_shared.ID, sa_shared.APP_NAME, sa_shared.DESCRIPTION, sa_shared.UUID, sa_shared.IMAGE_URL, " + + "CASE WHEN sa_shared.ACCESS_URL IS NOT NULL THEN sa_shared.ACCESS_URL ELSE sa_main.ACCESS_URL END AS " + + "ACCESS_URL, sa_shared.USERNAME, sa_shared.USER_STORE, sa_shared.TENANT_ID FROM SP_SHARED_APP ssa " + + "JOIN SP_APP sa_main ON ssa.MAIN_APP_ID = sa_main.UUID " + + "JOIN SP_APP sa_shared ON ssa.SHARED_APP_ID = sa_shared.UUID WHERE ssa.SHARED_ORG_ID = ? AND " + + "ssa.OWNER_ORG_ID = ? AND (sa_main.IS_DISCOVERABLE = '1' OR sa_shared.IS_DISCOVERABLE = '1') " + + "ORDER BY ID DESC SKIP ? LIMIT ?"; + + public static final String LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_MYSQL = + "SELECT sa_shared.ID, sa_shared.APP_NAME, sa_shared.DESCRIPTION, sa_shared.UUID, sa_shared.IMAGE_URL, " + + "CASE WHEN sa_shared.ACCESS_URL IS NOT NULL THEN sa_shared.ACCESS_URL ELSE sa_main.ACCESS_URL END AS " + + "ACCESS_URL, sa_shared.USERNAME, sa_shared.USER_STORE, sa_shared.TENANT_ID FROM SP_SHARED_APP ssa " + + "JOIN SP_APP sa_main ON ssa.MAIN_APP_ID = sa_main.UUID " + + "JOIN SP_APP sa_shared ON ssa.SHARED_APP_ID = sa_shared.UUID WHERE ssa.SHARED_ORG_ID = ? AND " + + "sa_shared.APP_NAME LIKE ? AND ssa.OWNER_ORG_ID = ? AND (sa_main.IS_DISCOVERABLE = '1' OR " + + "sa_shared.IS_DISCOVERABLE = '1') ORDER BY ID DESC LIMIT ?, ?"; + + public static final String LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_POSTGRESL = + "SELECT sa_shared.ID, sa_shared.APP_NAME, sa_shared.DESCRIPTION, sa_shared.UUID, sa_shared.IMAGE_URL, " + + "CASE WHEN sa_shared.ACCESS_URL IS NOT NULL THEN sa_shared.ACCESS_URL ELSE sa_main.ACCESS_URL END AS " + + "ACCESS_URL, sa_shared.USERNAME, sa_shared.USER_STORE, sa_shared.TENANT_ID FROM SP_SHARED_APP ssa " + + "JOIN SP_APP sa_main ON ssa.MAIN_APP_ID = sa_main.UUID " + + "JOIN SP_APP sa_shared ON ssa.SHARED_APP_ID = sa_shared.UUID WHERE ssa.SHARED_ORG_ID = ? AND " + + "sa_shared.APP_NAME LIKE ? AND ssa.OWNER_ORG_ID = ? AND (sa_main.IS_DISCOVERABLE = '1' OR " + + "sa_shared.IS_DISCOVERABLE = '1') ORDER BY ID DESC OFFSET ? LIMIT ?"; + + public static final String LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_ORACLE = + "SELECT sa_shared.ID, sa_shared.APP_NAME, sa_shared.DESCRIPTION, sa_shared.UUID, sa_shared.IMAGE_URL, " + + "CASE WHEN sa_shared.ACCESS_URL IS NOT NULL THEN sa_shared.ACCESS_URL ELSE sa_main.ACCESS_URL END AS " + + "ACCESS_URL, sa_shared.USERNAME, sa_shared.USER_STORE, sa_shared.TENANT_ID FROM SP_SHARED_APP ssa " + + "JOIN SP_APP sa_main ON ssa.MAIN_APP_ID = sa_main.UUID " + + "JOIN SP_APP sa_shared ON ssa.SHARED_APP_ID = sa_shared.UUID WHERE ssa.SHARED_ORG_ID = ? AND " + + "sa_shared.APP_NAME LIKE ? AND ssa.OWNER_ORG_ID = ? AND (sa_main.IS_DISCOVERABLE = '1' OR " + + "sa_shared.IS_DISCOVERABLE = '1') BETWEEN ? AND ? ORDER BY ID DESC"; + + public static final String LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_MSSQL = + "SELECT sa_shared.ID, sa_shared.APP_NAME, sa_shared.DESCRIPTION, sa_shared.UUID, sa_shared.IMAGE_URL, " + + "CASE WHEN sa_shared.ACCESS_URL IS NOT NULL THEN sa_shared.ACCESS_URL ELSE sa_main.ACCESS_URL END AS " + + "ACCESS_URL, sa_shared.USERNAME, sa_shared.USER_STORE, sa_shared.TENANT_ID FROM SP_SHARED_APP ssa " + + "JOIN SP_APP sa_main ON ssa.MAIN_APP_ID = sa_main.UUID " + + "JOIN SP_APP sa_shared ON ssa.SHARED_APP_ID = sa_shared.UUID WHERE ssa.SHARED_ORG_ID = ? AND " + + "sa_shared.APP_NAME LIKE ? AND ssa.OWNER_ORG_ID = ? AND (sa_main.IS_DISCOVERABLE = '1' OR " + + "sa_shared.IS_DISCOVERABLE = '1') ORDER BY ID DESC OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; + + public static final String LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_INFORMIX = + "SELECT sa_shared.ID, sa_shared.APP_NAME, sa_shared.DESCRIPTION, sa_shared.UUID, sa_shared.IMAGE_URL, " + + "CASE WHEN sa_shared.ACCESS_URL IS NOT NULL THEN sa_shared.ACCESS_URL ELSE sa_main.ACCESS_URL END AS " + + "ACCESS_URL, sa_shared.USERNAME, sa_shared.USER_STORE, sa_shared.TENANT_ID FROM SP_SHARED_APP ssa " + + "JOIN SP_APP sa_main ON ssa.MAIN_APP_ID = sa_main.UUID " + + "JOIN SP_APP sa_shared ON ssa.SHARED_APP_ID = sa_shared.UUID WHERE ssa.SHARED_ORG_ID = ? AND " + + "sa_shared.APP_NAME LIKE ? AND ssa.OWNER_ORG_ID = ? AND (sa_main.IS_DISCOVERABLE = '1' OR " + + "sa_shared.IS_DISCOVERABLE = '1') ORDER BY ID DESC SKIP ? LIMIT ?"; + + public static final String LOAD_DISCOVERABLE_SHARED_APP_COUNT_BY_TENANT = + "SELECT COUNT(sa_shared.UUID) FROM SP_SHARED_APP ssa " + + "JOIN SP_APP sa_main ON ssa.MAIN_APP_ID = sa_main.UUID " + + "JOIN SP_APP sa_shared ON ssa.SHARED_APP_ID = sa_shared.UUID WHERE ssa.SHARED_ORG_ID = ? AND " + + "ssa.OWNER_ORG_ID = ? AND (sa_main.IS_DISCOVERABLE = '1' OR sa_shared.IS_DISCOVERABLE = '1') "; + + public static final String LOAD_DISCOVERABLE_SHARED_APP_COUNT_BY_APP_NAME_AND_TENANT = + "SELECT COUNT(sa_shared.UUID) FROM SP_SHARED_APP ssa " + + "JOIN SP_APP sa_main ON ssa.MAIN_APP_ID = sa_main.UUID " + + "JOIN SP_APP sa_shared ON ssa.SHARED_APP_ID = sa_shared.UUID WHERE ssa.SHARED_ORG_ID = ? AND " + + "sa_shared.APP_NAME LIKE ? AND ssa.OWNER_ORG_ID = ? AND (sa_main.IS_DISCOVERABLE = '1' OR " + + "sa_shared.IS_DISCOVERABLE = '1')"; + private SQLConstants() { } diff --git a/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/dao/OrgApplicationMgtDAO.java b/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/dao/OrgApplicationMgtDAO.java index 1f5ffb9a..0ddd9081 100644 --- a/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/dao/OrgApplicationMgtDAO.java +++ b/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/dao/OrgApplicationMgtDAO.java @@ -18,6 +18,7 @@ package org.wso2.carbon.identity.organization.management.application.dao; +import org.wso2.carbon.identity.application.common.model.ApplicationBasicInfo; 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; @@ -138,4 +139,36 @@ default List getSharedApplications(String mainAppId, String throw new NotImplementedException( "getSharedApplications method is not implemented in " + this.getClass().getName()); } + + /** + * Returns the basic information of the discoverable shared applications + * + * @param limit Maximum no of applications to be returned in the result set (optional). + * @param offset Zero based index of the first application to be returned in the result set (optional). + * @param filter Filter to search for applications (optional). + * @param sortOrder Sort order, ascending or descending (optional). + * @param sortBy Attribute to sort from (optional). + * @param tenantDomain Tenant domain. + * @param rootOrgId Root organization ID. + * @return List of DiscoverableApplicationBasicInfo of applications matching the given criteria. + * @throws OrganizationManagementException The server exception is thrown in a failure when retrieving the + * discoverable applications. + */ + List getDiscoverableSharedApplicationBasicInfo(int limit, int offset, String filter, + String sortOrder, String sortBy, + String tenantDomain, String rootOrgId) + throws OrganizationManagementException; + + /** + * Returns the count of discoverable shared applications matching given filter. + * + * @param filter Filter to search for applications (optional). + * @param tenantDomain Tenant domain. + * @param rootOrgId Root organization ID. + * @return Count of discoverable applications matching given filter. + * @throws OrganizationManagementException The server exception is thrown in a failure when retrieving the + * discoverable applications count. + */ + int getCountOfDiscoverableSharedApplications(String filter, String tenantDomain, String rootOrgId) + throws OrganizationManagementException; } diff --git a/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/dao/impl/OrgApplicationMgtDAOImpl.java b/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/dao/impl/OrgApplicationMgtDAOImpl.java index 2a7c7b5d..9bfd73f2 100644 --- a/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/dao/impl/OrgApplicationMgtDAOImpl.java +++ b/components/org.wso2.carbon.identity.organization.management.application/src/main/java/org/wso2/carbon/identity/organization/management/application/dao/impl/OrgApplicationMgtDAOImpl.java @@ -19,22 +19,43 @@ package org.wso2.carbon.identity.organization.management.application.dao.impl; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.wso2.carbon.database.utils.jdbc.NamedJdbcTemplate; +import org.wso2.carbon.database.utils.jdbc.NamedPreparedStatement; import org.wso2.carbon.database.utils.jdbc.exceptions.DataAccessException; import org.wso2.carbon.database.utils.jdbc.exceptions.TransactionException; +import org.wso2.carbon.identity.application.common.model.ApplicationBasicInfo; +import org.wso2.carbon.identity.application.common.model.User; +import org.wso2.carbon.identity.application.mgt.ApplicationConstants; +import org.wso2.carbon.identity.application.mgt.ApplicationMgtUtil; +import org.wso2.carbon.identity.core.URLBuilderException; +import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.JdbcUtils; import org.wso2.carbon.identity.organization.management.application.dao.OrgApplicationMgtDAO; 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.OrganizationManagementClientException; import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementServerException; +import org.wso2.carbon.utils.multitenancy.MultitenantConstants; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; 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.application.common.util.IdentityApplicationConstants.Error.INVALID_OFFSET; +import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.Error.SORTING_NOT_IMPLEMENTED; +import static org.wso2.carbon.identity.application.mgt.ApplicationMgtUtil.getConsoleAccessUrlFromServerConfig; +import static org.wso2.carbon.identity.application.mgt.ApplicationMgtUtil.getMyAccountAccessUrlFromServerConfig; 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; @@ -45,6 +66,18 @@ import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.INSERT_SHARED_APP; import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.IS_FRAGMENT_APPLICATION; import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.IS_FRAGMENT_APPLICATION_H2; +import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_INFORMIX; +import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_MSSQL; +import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_MYSQL; +import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_ORACLE; +import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_POSTGRESL; +import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_INFORMIX; +import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_MSSQL; +import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_MYSQL; +import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_ORACLE; +import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_POSTGRES; +import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.LOAD_DISCOVERABLE_SHARED_APP_COUNT_BY_APP_NAME_AND_TENANT; +import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.LOAD_DISCOVERABLE_SHARED_APP_COUNT_BY_TENANT; import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.SQLPlaceholders.DB_SCHEMA_COLUMN_NAME_MAIN_APP_ID; import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.SQLPlaceholders.DB_SCHEMA_COLUMN_NAME_METADATA_NAME; import static org.wso2.carbon.identity.organization.management.application.constant.SQLConstants.SQLPlaceholders.DB_SCHEMA_COLUMN_NAME_METADATA_VALUE; @@ -72,6 +105,9 @@ */ public class OrgApplicationMgtDAOImpl implements OrgApplicationMgtDAO { + private static final Log log = LogFactory.getLog(OrgApplicationMgtDAOImpl.class); + private static final String ASTERISK = "*"; + @Override public void addSharedApplication(String mainAppId, String ownerOrgId, String sharedAppId, String sharedOrgId, boolean shareWithAllChildren) throws OrganizationManagementException { @@ -264,6 +300,140 @@ public List getSharedApplications(String mainAppId, String } } + @Override + public List getDiscoverableSharedApplicationBasicInfo(int limit, int offset, String filter, + String sortOrder, String sortBy, + String tenantDomain, String rootOrgId) + throws OrganizationManagementException { + + validateForUnImplementedSortingAttributes(sortOrder, sortBy); + validateAttributesForPagination(offset, limit); + + // TODO: 17/9/24 : Enforce a max limit + if (StringUtils.isBlank(filter) || ASTERISK.equals(filter)) { + return getDiscoverableSharedApplicationBasicInfo(limit, offset, tenantDomain, rootOrgId); + } + + String filterResolvedForSQL = resolveSQLFilter(filter); + + List applicationBasicInfoList = new ArrayList<>(); + + try (Connection connection = IdentityDatabaseUtil.getDBConnection(false)) { + String databaseVendorType = connection.getMetaData().getDatabaseProductName(); + + try (NamedPreparedStatement statement = + new NamedPreparedStatement(connection, + getDBVendorSpecificDiscoverableSharedAppRetrievalQueryByAppName(databaseVendorType))) { + statement.setString(1, tenantDomain); + statement.setString(2, filterResolvedForSQL); + statement.setString(3, rootOrgId); + statement.setInt(4, offset); + statement.setInt(5, limit); + + try (ResultSet resultSet = statement.executeQuery()) { + while (resultSet.next()) { + applicationBasicInfoList.add(buildApplicationBasicInfo(resultSet)); + } + } + } + } catch (SQLException e) { + throw new OrganizationManagementException("Error while getting application basic information" + + " for discoverable applications in tenantDomain: " + tenantDomain, e); + } + return Collections.unmodifiableList(applicationBasicInfoList); + } + + @Override + public int getCountOfDiscoverableSharedApplications(String filter, String tenantDomain, String rootOrgId) + throws OrganizationManagementException { + + if (log.isDebugEnabled()) { + log.debug("Getting count of discoverable shared applications matching filter: " + filter + " in " + + "tenantDomain: " + tenantDomain); + } + + if (StringUtils.isBlank(filter) || ASTERISK.equals(filter)) { + return getCountOfDiscoverableSharedApplications(tenantDomain, rootOrgId); + } + + int count = 0; + String filterResolvedForSQL = resolveSQLFilter(filter); + try (Connection connection = IdentityDatabaseUtil.getDBConnection(false)) { + + try (NamedPreparedStatement statement = + new NamedPreparedStatement(connection, + LOAD_DISCOVERABLE_SHARED_APP_COUNT_BY_APP_NAME_AND_TENANT)) { + statement.setString(1, tenantDomain); + statement.setString(2, filterResolvedForSQL); + statement.setString(3, rootOrgId); + + try (ResultSet resultSet = statement.executeQuery()) { + if (resultSet.next()) { + count = resultSet.getInt(1); + } + } + } + } catch (SQLException e) { + throw new OrganizationManagementServerException("Error while getting count of discoverable " + + "applications matching filter:" + filter + " in tenantDomain: " + tenantDomain); + } + return count; + } + + private int getCountOfDiscoverableSharedApplications(String tenantDomain, String rootOrgId) + throws OrganizationManagementException { + + int count; + try (Connection connection = IdentityDatabaseUtil.getDBConnection(false)) { + + try (NamedPreparedStatement statement = + new NamedPreparedStatement(connection, + LOAD_DISCOVERABLE_SHARED_APP_COUNT_BY_TENANT)) { + statement.setString(1, tenantDomain); + statement.setString(2, rootOrgId); + + try (ResultSet resultSet = statement.executeQuery()) { + resultSet.next(); + count = resultSet.getInt(1); + } + } + } catch (SQLException e) { + throw new OrganizationManagementServerException("Error while getting count of discoverable " + + "shared applications in tenantDomain: " + tenantDomain); + } + return count; + } + + private List getDiscoverableSharedApplicationBasicInfo(int limit, int offset, + String tenantDomain, String rootOrgId) + throws OrganizationManagementException { + + List applicationBasicInfoList = new ArrayList<>(); + + try (Connection connection = IdentityDatabaseUtil.getDBConnection(false)) { + String databaseVendorType = connection.getMetaData().getDatabaseProductName(); + + try (NamedPreparedStatement statement = + new NamedPreparedStatement(connection, + getDBVendorSpecificDiscoverableSharedAppRetrievalQuery(databaseVendorType))) { + statement.setString(1, tenantDomain); + statement.setString(2, rootOrgId); + statement.setInt(3, offset); + statement.setInt(4, limit); + + try (ResultSet resultSet = statement.executeQuery()) { + while (resultSet.next()) { + applicationBasicInfoList.add(buildApplicationBasicInfo(resultSet)); + } + } + } + } catch (SQLException e) { + throw new OrganizationManagementException("Error while getting application basic information" + + " for discoverable applications in tenantDomain: " + tenantDomain, e); + } + return Collections.unmodifiableList(applicationBasicInfoList); + } + /** * Get the value for the shareWithAllChildren column according to the db type. * @@ -279,4 +449,153 @@ private String getShareWithAllChildrenValue(boolean shareWithAllChildren) } return String.valueOf(shareWithAllChildren); } + + private void validateForUnImplementedSortingAttributes(String sortOrder, String sortBy) + throws OrganizationManagementServerException { + + if (StringUtils.isNotBlank(sortBy) || StringUtils.isNotBlank(sortOrder)) { + throw new OrganizationManagementServerException(SORTING_NOT_IMPLEMENTED.getCode(), + "Sorting not supported."); + } + } + + /** + * Validates the offset and limit values for pagination. + * + * @param offset Starting index. + * @param limit Count value. + * @throws OrganizationManagementClientException + */ + private void validateAttributesForPagination(int offset, int limit) + throws OrganizationManagementClientException { + + if (offset < 0) { + throw new OrganizationManagementClientException("Invalid offset requested.", + "Invalid offset requested. Offset value should be zero or greater than zero.", + INVALID_OFFSET.getCode()); + } + + if (limit <= 0) { + throw new OrganizationManagementClientException("Invalid limit requested.", + "Invalid limit requested. Limit value should be greater than zero.", + INVALID_OFFSET.getCode()); + } + } + + private ApplicationBasicInfo buildApplicationBasicInfo(ResultSet appNameResultSet) + throws SQLException, OrganizationManagementException { + + /* + * If you add a new value to basicInfo here, please consider to add it in the + * buildApplicationBasicInfoWithInboundConfig() function also. + */ + ApplicationBasicInfo basicInfo = new ApplicationBasicInfo(); + basicInfo.setApplicationId(appNameResultSet.getInt(ApplicationConstants.ApplicationTableColumns.ID)); + basicInfo.setApplicationName(appNameResultSet.getString(ApplicationConstants.ApplicationTableColumns.APP_NAME)); + basicInfo.setDescription(appNameResultSet.getString(ApplicationConstants.ApplicationTableColumns.DESCRIPTION)); + + basicInfo.setApplicationResourceId(appNameResultSet.getString(ApplicationConstants + .ApplicationTableColumns.UUID)); + basicInfo.setImageUrl(appNameResultSet.getString(ApplicationConstants.ApplicationTableColumns.IMAGE_URL)); + + try { + basicInfo.setAccessUrl(appNameResultSet.getString(ApplicationConstants.ApplicationTableColumns.ACCESS_URL)); + if (ApplicationMgtUtil.isConsoleOrMyAccount(basicInfo.getApplicationName())) { + basicInfo.setAccessUrl(ApplicationMgtUtil.resolveOriginUrlFromPlaceholders( + appNameResultSet.getString(ApplicationConstants.ApplicationTableColumns.ACCESS_URL), + basicInfo.getApplicationName())); + } + } catch (URLBuilderException e) { + throw new OrganizationManagementException( + "Error occurred when resolving origin of the access URL with placeholders", e); + } + String tenantDomain = IdentityTenantUtil.getTenantDomain(appNameResultSet.getInt( + ApplicationConstants.ApplicationTableColumns.TENANT_ID)); + if (ApplicationMgtUtil.isConsole(basicInfo.getApplicationName())) { + String consoleAccessUrl = getConsoleAccessUrlFromServerConfig(tenantDomain); + if (StringUtils.isNotBlank(consoleAccessUrl)) { + basicInfo.setAccessUrl(consoleAccessUrl); + } + } + if (ApplicationMgtUtil.isMyAccount(basicInfo.getApplicationName())) { + String myAccountAccessUrl = getMyAccountAccessUrlFromServerConfig(tenantDomain); + if (StringUtils.isNotBlank(myAccountAccessUrl)) { + basicInfo.setAccessUrl(myAccountAccessUrl); + } + } + + String username = appNameResultSet.getString(ApplicationConstants.ApplicationTableColumns.USERNAME); + String userStoreDomain = appNameResultSet.getString(ApplicationConstants.ApplicationTableColumns.USER_STORE); + int tenantId = appNameResultSet.getInt(ApplicationConstants.ApplicationTableColumns.TENANT_ID); + + if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(userStoreDomain) + && !(tenantId == MultitenantConstants.INVALID_TENANT_ID)) { + User appOwner = new User(); + appOwner.setUserStoreDomain(userStoreDomain); + appOwner.setUserName(username); + appOwner.setTenantDomain(IdentityTenantUtil.getTenantDomain(tenantId)); + + basicInfo.setAppOwner(appOwner); + } + + return basicInfo; + } + + private String resolveSQLFilter(String filter) { + + //To avoid any issues when the filter string is blank or null, assigning "%" to SQLFilter. + String sqlfilter = "SP_APP.APP_NAME LIKE '%'"; + if (StringUtils.isNotBlank(filter)) { + sqlfilter = filter.trim() + .replace(ASTERISK, "%") + .replace("?", "_"); + } + + if (log.isDebugEnabled()) { + log.debug("Input filter: " + filter + " resolved for SQL filter: " + sqlfilter); + } + return sqlfilter; + } + + private String getDBVendorSpecificDiscoverableSharedAppRetrievalQuery(String dbVendorType) + throws OrganizationManagementServerException { + + if ("MySQL".equals(dbVendorType) || "MariaDB".equals(dbVendorType) + || "H2".equals(dbVendorType) + || "DB2".equals(dbVendorType)) { + return LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_MYSQL; + } else if ("Oracle".equals(dbVendorType)) { + return LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_ORACLE; + } else if ("PostgreSQL".equals(dbVendorType)) { + return LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_POSTGRES; + } else if ("Microsoft SQL Server".equals(dbVendorType)) { + return LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_MSSQL; + } else if ("INFORMIX".equals(dbVendorType)) { + return LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_INFORMIX; + } + + throw new OrganizationManagementServerException("Error while loading discoverable applications from " + + "DB. Database driver for " + dbVendorType + "could not be identified or not supported."); + } + + private String getDBVendorSpecificDiscoverableSharedAppRetrievalQueryByAppName(String dbVendorType) + throws OrganizationManagementServerException { + + if ("MySQL".equals(dbVendorType) || "MariaDB".equals(dbVendorType) + || "H2".equals(dbVendorType) + || "DB2".equals(dbVendorType)) { + return LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_MYSQL; + } else if ("Oracle".equals(dbVendorType)) { + return LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_ORACLE; + } else if ("PostgreSQL".equals(dbVendorType)) { + return LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_POSTGRESL; + } else if ("Microsoft SQL Server".equals(dbVendorType)) { + return LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_MSSQL; + } else if ("INFORMIX".equals(dbVendorType)) { + return LOAD_DISCOVERABLE_SHARED_APPS_BY_TENANT_AND_APP_NAME_INFORMIX; + } + + throw new OrganizationManagementServerException("Error while loading discoverable applications from " + + "DB. Database driver for " + dbVendorType + "could not be identified or not supported."); + } }