From 7bd1857f45c53ca36b1aa8ad5e3c14102a14ba6b Mon Sep 17 00:00:00 2001 From: SujanSanjula96 Date: Wed, 18 Oct 2023 21:19:23 +0530 Subject: [PATCH] Add support for API Resource properties --- .../api/resource/mgt/APIResourceManager.java | 18 ++ .../resource/mgt/APIResourceManagerImpl.java | 16 ++ .../APIResourceManagementConstants.java | 5 + .../resource/mgt/constant/SQLConstants.java | 47 ++++ .../mgt/dao/APIResourceManagementDAO.java | 16 ++ .../impl/APIResourceManagementDAOImpl.java | 232 +++++++++++++++++- .../impl/CacheBackedAPIResourceMgtDAO.java | 10 + .../src/test/resources/dbscripts/h2.sql | 10 + .../application/common/model/APIResource.java | 19 ++ .../common/model/APIResourceProperty.java | 48 ++++ .../resources/dbscripts/db2.sql | 21 ++ .../resources/dbscripts/h2.sql | 10 + .../resources/dbscripts/mssql.sql | 11 + .../resources/dbscripts/mysql-cluster.sql | 11 + .../resources/dbscripts/mysql.sql | 10 + .../resources/dbscripts/postgresql.sql | 13 + 16 files changed, 484 insertions(+), 13 deletions(-) create mode 100644 components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/APIResourceProperty.java diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceManager.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceManager.java index 0bdeefc940e8..70f9fe3af19e 100644 --- a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceManager.java +++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceManager.java @@ -45,6 +45,24 @@ APIResourceSearchResult getAPIResources(String after, String before, Integer lim String tenantDomain) throws APIResourceMgtException; + /** + * Get API resources with required attributes. + * + * @param after Get API resources after this value. + * @param before Get API resources before this value. + * @param limit Number of API resources to retrieve. + * @param filter Filter expression. + * @param sortOrder Sort order. + * @param tenantDomain Tenant domain. + * @param requiredAttributes Required attributes. + * @return API resource search result with required attributes. + * @throws APIResourceMgtException If an error occurs while retrieving API resources with required attributes. + */ + APIResourceSearchResult getAPIResourcesWithRequiredAttributes(String after, String before, Integer limit, + String filter, String sortOrder, + String tenantDomain, List requiredAttributes) + throws APIResourceMgtException; + /** * Get API resource by id. * diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceManagerImpl.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceManagerImpl.java index fb13f8796f2e..24e3d56086f3 100644 --- a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceManagerImpl.java +++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceManagerImpl.java @@ -71,6 +71,22 @@ public APIResourceSearchResult getAPIResources(String after, String before, Inte return result; } + @Override + public APIResourceSearchResult getAPIResourcesWithRequiredAttributes(String after, String before, Integer limit, + String filter, String sortOrder, + String tenantDomain, + List requiredAttributes) + throws APIResourceMgtException { + + APIResourceSearchResult result = new APIResourceSearchResult(); + List expressionNodes = getExpressionNodes(filter, after, before); + int tenantId = IdentityTenantUtil.getTenantId(tenantDomain); + result.setTotalCount(CACHE_BACKED_DAO.getAPIResourcesCount(tenantId, expressionNodes)); + result.setAPIResources(CACHE_BACKED_DAO.getAPIResourcesWithRequiredAttributes(limit, tenantId, sortOrder, + expressionNodes, requiredAttributes)); + return result; + } + @Override public APIResource getAPIResourceById(String apiResourceId, String tenantDomain) throws APIResourceMgtException { diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/constant/APIResourceManagementConstants.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/constant/APIResourceManagementConstants.java index 25b2f84bcc8a..5df085fd3a09 100644 --- a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/constant/APIResourceManagementConstants.java +++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/constant/APIResourceManagementConstants.java @@ -32,6 +32,7 @@ public class APIResourceManagementConstants { public static final String TYPE = "type"; public static final String BEFORE = "before"; public static final String AFTER = "after"; + public static final String PROPERTIES = "properties"; public static final String EQ = "eq"; public static final String CO = "co"; public static final String SW = "sw"; @@ -98,6 +99,10 @@ public enum ErrorMessages { " scope.", "Error while checking existence of scope in the database."), ERROR_CODE_ERROR_WHILE_CHECKING_API_RESOURCE_EXISTENCE("65011", "Error while checking existence " + "of API resource.", "Error while checking existence of API resource in the database."), + ERROR_CODE_ERROR_WHILE_RETRIEVING_API_RESOURCE_PROPERTIES("65013", "Error while retrieving API " + + "resource properties.", "Error while retrieving API resource properties from the database."), + ERROR_CODE_ERROR_WHILE_ADDING_API_RESOURCE_PROPERTIES("65014", "Error while adding API resource " + + "properties.", "Error while adding API resource properties to the database."), ; private final String code; diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/constant/SQLConstants.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/constant/SQLConstants.java index 9472d6c43d6a..2175d192cb31 100644 --- a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/constant/SQLConstants.java +++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/constant/SQLConstants.java @@ -26,6 +26,7 @@ public class SQLConstants { // DB types. public static final String MICROSOFT = "Microsoft"; public static final String DB2 = "DB2"; + public static final String H2 = "H2"; // Column names. public static final String ID_COLUMN_NAME = "ID"; @@ -37,6 +38,7 @@ public class SQLConstants { public static final String TYPE_COLUMN_NAME = "TYPE"; public static final String REQUIRES_AUTHORIZATION_COLUMN_NAME = "REQUIRES_AUTHORIZATION"; public static final String DISPLAY_NAME_COLUMN_NAME = "DISPLAY_NAME"; + public static final String VALUE_COLUMN_NAME = "VALUE"; public static final String API_RESOURCE_ID_COLUMN_NAME = "API_RESOURCE_ID"; public static final String API_RESOURCE_NAME_COLUMN_NAME = "API_RESOURCE_NAME"; public static final String API_RESOURCE_IDENTIFIER_COLUMN_NAME = "API_RESOURCE_IDENTIFIER"; @@ -47,6 +49,9 @@ public class SQLConstants { public static final String SCOPE_QUALIFIED_NAME_COLUMN_NAME = "SCOPE_QUALIFIED_NAME"; public static final String SCOPE_DISPLAY_NAME_COLUMN_NAME = "SCOPE_DISPLAY_NAME"; public static final String SCOPE_DESCRIPTION_COLUMN_NAME = "SCOPE_DESCRIPTION"; + public static final String API_RESOURCE_PROPERTY_ID_COLUMN_NAME = "PROPERTY_ID"; + public static final String API_RESOURCE_PROPERTY_NAME_COLUMN_NAME = "PROPERTY_NAME"; + public static final String API_RESOURCE_PROPERTY_VALUE_COLUMN_NAME = "PROPERTY_VALUE"; // Database constraint names. public static final String API_RESOURCE_UNIQUE_CONSTRAINT = "identifier_unique"; @@ -62,6 +67,34 @@ public class SQLConstants { " TENANT_ID = %d ORDER BY CURSOR_KEY %s LIMIT %d"; public static final String GET_API_RESOURCES_TAIL_MSSQL = " TENANT_ID = %d ORDER BY CURSOR_KEY %s"; + public static final String GET_API_RESOURCES_WITH_PROPERTIES_SELECTION = "SELECT" + + " AR.ID AS API_RESOURCE_ID," + + " AR.CURSOR_KEY AS CURSOR_KEY," + + " AR.NAME AS API_RESOURCE_NAME," + + " AR.IDENTIFIER AS API_RESOURCE_IDENTIFIER," + + " AR.DESCRIPTION AS API_RESOURCE_DESCRIPTION," + + " AR.TENANT_ID AS API_RESOURCE_TENANT_ID," + + " AR.TYPE AS API_RESOURCE_TYPE," + + " AR.REQUIRES_AUTHORIZATION AS REQUIRES_AUTHORIZATION," + + " ARP.ID AS PROPERTY_ID," + + " ARP.NAME AS PROPERTY_NAME," + + " ARP.VALUE AS PROPERTY_VALUE" + + " FROM ("; + public static final String GET_API_RESOURCES_WITH_PROPERTIES_SELECTION_H2 = "SELECT" + + " AR.ID AS API_RESOURCE_ID," + + " AR.CURSOR_KEY AS CURSOR_KEY," + + " AR.NAME AS API_RESOURCE_NAME," + + " AR.IDENTIFIER AS API_RESOURCE_IDENTIFIER," + + " AR.DESCRIPTION AS API_RESOURCE_DESCRIPTION," + + " AR.TENANT_ID AS API_RESOURCE_TENANT_ID," + + " AR.TYPE AS API_RESOURCE_TYPE," + + " AR.REQUIRES_AUTHORIZATION AS REQUIRES_AUTHORIZATION," + + " ARP.ID AS PROPERTY_ID," + + " ARP.NAME AS PROPERTY_NAME," + + " ARP.`VALUE` AS PROPERTY_VALUE" + + " FROM ("; + public static final String GET_API_RESOURCES_WITH_PROPERTIES_JOIN = ") AR" + + " LEFT JOIN API_RESOURCE_PROPERTY ARP ON AR.ID = ARP.API_ID ORDER BY CURSOR_KEY %s"; public static final String GET_API_RESOURCES_COUNT = "SELECT COUNT(DISTINCT(ID)) FROM API_RESOURCE WHERE "; public static final String GET_API_RESOURCES_COUNT_TAIL = " TENANT_ID = ?"; public static final String GET_API_RESOURCE_BY_ID = "SELECT" + @@ -111,4 +144,18 @@ public class SQLConstants { "TENANT_ID FROM SCOPE WHERE "; public static final String GET_SCOPES_BY_TENANT_ID_TAIL = " TENANT_ID = ?"; public static final String DELETE_SCOPE_BY_NAME = "DELETE FROM SCOPE WHERE NAME = ? AND TENANT_ID = ?"; + public static final String GET_API_RESOURCE_PROPERTIES_BY_API_ID = "SELECT ID, NAME, VALUE FROM " + + "API_RESOURCE_PROPERTY WHERE API_ID = ?"; + public static final String GET_API_RESOURCE_PROPERTIES_BY_API_ID_H2 = "SELECT ID, NAME, `VALUE` FROM " + + "API_RESOURCE_PROPERTY WHERE API_ID = ?"; + public static final String GET_API_RESOURCE_PROPERTIES_BY_API_IDENTIFIER = "SELECT ARP.ID, ARP.NAME, ARP.VALUE " + + "FROM API_RESOURCE AR JOIN API_RESOURCE_PROPERTY ARP ON AR.ID = ARP.API_ID WHERE AR.IDENTIFIER = ? " + + "AND AR.TENANT_ID = ?"; + public static final String GET_API_RESOURCE_PROPERTIES_BY_API_IDENTIFIER_H2 = "SELECT ARP.ID, ARP.NAME, " + + "ARP.`VALUE` FROM API_RESOURCE AR JOIN API_RESOURCE_PROPERTY ARP ON AR.ID = ARP.API_ID WHERE " + + "AR.IDENTIFIER = ? AND AR.TENANT_ID = ?"; + public static final String ADD_API_RESOURCE_PROPERTY = "INSERT INTO API_RESOURCE_PROPERTY (API_ID, NAME, VALUE) " + + "VALUES (?, ?, ?)"; + public static final String ADD_API_RESOURCE_PROPERTY_H2 = "INSERT INTO API_RESOURCE_PROPERTY (API_ID, NAME, " + + "`VALUE`) VALUES (?, ?, ?)"; } diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/dao/APIResourceManagementDAO.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/dao/APIResourceManagementDAO.java index 60ca1557ff20..38e7ae419f3f 100644 --- a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/dao/APIResourceManagementDAO.java +++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/dao/APIResourceManagementDAO.java @@ -45,6 +45,22 @@ public interface APIResourceManagementDAO { List getAPIResources(Integer limit, Integer tenantId, String sortOrder, List expressionNodes) throws APIResourceMgtException; + /** + * Retrieve the API resources under a given tenantId. + * + * @param limit Maximum number of records to return. + * @param tenantId Tenant Id. + * @param sortOrder Sort order for the cursor based pagination. + * @param expressionNodes Expression nodes. + * @param requiredAttributes List of required attributes + * @return List of APIResource. + * @throws APIResourceMgtException If an error occurs while retrieving the API resources. + */ + List getAPIResourcesWithRequiredAttributes(Integer limit, Integer tenantId, String sortOrder, + List expressionNodes, + List requiredAttributes) + throws APIResourceMgtException; + /** * Retrieve the count of API resources under a given tenantId. * diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/dao/impl/APIResourceManagementDAOImpl.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/dao/impl/APIResourceManagementDAOImpl.java index 4a072355566f..17e48e987138 100644 --- a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/dao/impl/APIResourceManagementDAOImpl.java +++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/dao/impl/APIResourceManagementDAOImpl.java @@ -29,6 +29,7 @@ import org.wso2.carbon.identity.api.resource.mgt.model.FilterQueryBuilder; import org.wso2.carbon.identity.api.resource.mgt.util.APIResourceManagementUtil; import org.wso2.carbon.identity.application.common.model.APIResource; +import org.wso2.carbon.identity.application.common.model.APIResourceProperty; import org.wso2.carbon.identity.application.common.model.ApplicationBasicInfo; import org.wso2.carbon.identity.application.common.model.Scope; import org.wso2.carbon.identity.core.model.ExpressionNode; @@ -39,6 +40,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -59,6 +61,19 @@ public List getAPIResources(Integer limit, Integer tenantId, String return getAPIResourcesList(limit, tenantId, sortOrder, expressionNodes); } + @Override + public List getAPIResourcesWithRequiredAttributes(Integer limit, Integer tenantId, String sortOrder, + List expressionNodes, + List requiredAttributes) + throws APIResourceMgtException { + + if (CollectionUtils.isEmpty(requiredAttributes) || + !requiredAttributes.contains(APIResourceManagementConstants.PROPERTIES)) { + return getAPIResourcesList(limit, tenantId, sortOrder, expressionNodes); + } + return getAPIResourcesListWithProperties(limit, tenantId, sortOrder, expressionNodes); + } + @Override public Integer getAPIResourcesCount(Integer tenantId, List expressionNodes) throws APIResourceMgtException { @@ -124,6 +139,10 @@ public APIResource addAPIResource(APIResource apiResource, Integer tenantId) thr // Add scopes. addScopes(dbConnection, generatedAPIId, apiResource.getScopes(), tenantId); } + if (CollectionUtils.isNotEmpty(apiResource.getProperties())) { + // Add properties. + addAPIResourceProperties(dbConnection, generatedAPIId, apiResource.getProperties()); + } IdentityDatabaseUtil.commitTransaction(dbConnection); return getAPIResourceById(generatedAPIId, tenantId); @@ -224,7 +243,8 @@ public APIResource getAPIResourceById(String apiId, Integer tenantId) throws API preparedStatement.setString(1, apiId); preparedStatement.setInt(2, tenantId); ResultSet resultSet = preparedStatement.executeQuery(); - return getApiResource(resultSet); + List apiResourceProperties = getAPIResourcePropertiesByAPIId(dbConnection, apiId); + return getApiResource(resultSet, apiResourceProperties); } catch (SQLException e) { throw APIResourceManagementUtil.handleServerException( APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_RETRIEVING_API_RESOURCES, e); @@ -241,7 +261,9 @@ public APIResource getAPIResourceByIdentifier(String identifier, Integer tenantI preparedStatement.setString(1, identifier); preparedStatement.setInt(2, tenantId); ResultSet resultSet = preparedStatement.executeQuery(); - return getApiResource(resultSet); + List apiResourceProperties = + getAPIResourcePropertiesByAPIIdentifier(dbConnection, identifier); + return getApiResource(resultSet, apiResourceProperties); } catch (SQLException e) { throw APIResourceManagementUtil.handleServerException( APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_RETRIEVING_API_RESOURCES, e); @@ -511,8 +533,8 @@ private List getAPIResourcesList(Integer limit, Integer tenantId, S try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false)) { String databaseName = dbConnection.getMetaData().getDatabaseProductName(); - String sqlStmt = buildAPIResourcesSqlStatement(databaseName, tenantId, filterQueryBuilder.getFilterQuery(), - sortOrder, limit); + String sqlStmt = buildGetAPIResourcesSqlStatement(databaseName, tenantId, + filterQueryBuilder.getFilterQuery(), sortOrder, limit); PreparedStatement prepStmt = dbConnection.prepareStatement(sqlStmt); if (filterAttributeValue != null) { @@ -541,6 +563,71 @@ private List getAPIResourcesList(Integer limit, Integer tenantId, S return apiResources; } + /** + * Get API resources list. + * + * @param limit API resources limit. + * @param tenantId Tenant ID. + * @param sortOrder Order to sort the results. + * @param expressionNodes Expression nodes. + * @return API resources list. + * @throws APIResourceMgtException If an error occurs while retrieving API resources. + */ + private List getAPIResourcesListWithProperties(Integer limit, Integer tenantId, String sortOrder, + List expressionNodes) + throws APIResourceMgtException { + + FilterQueryBuilder filterQueryBuilder = new FilterQueryBuilder(); + appendFilterQuery(expressionNodes, filterQueryBuilder, false); + Map filterAttributeValue = filterQueryBuilder.getFilterAttributeValue(); + + Map apiResourceMap = new LinkedHashMap<>(); + try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false)) { + + String databaseName = dbConnection.getMetaData().getDatabaseProductName(); + String sqlStmt = buildGetAPIResourcesWithPropertiesSqlStatement(databaseName, tenantId, + filterQueryBuilder.getFilterQuery(), sortOrder, limit); + PreparedStatement prepStmt = dbConnection.prepareStatement(sqlStmt); + + if (filterAttributeValue != null) { + for (Map.Entry entry : filterAttributeValue.entrySet()) { + prepStmt.setString(entry.getKey(), entry.getValue()); + } + } + + ResultSet rs = prepStmt.executeQuery(); + while (rs.next()) { + String apiResourceId = rs.getString(SQLConstants.API_RESOURCE_ID_COLUMN_NAME); + if (!apiResourceMap.containsKey(apiResourceId)) { + List apiResourceProperties = new ArrayList<>(); + APIResource.APIResourceBuilder apiResourceBuilder = new APIResource.APIResourceBuilder() + .id(rs.getString(SQLConstants.API_RESOURCE_ID_COLUMN_NAME)) + .cursorKey(rs.getInt(SQLConstants.CURSOR_KEY_COLUMN_NAME)) + .name(rs.getString(SQLConstants.API_RESOURCE_NAME_COLUMN_NAME)) + .identifier(rs.getString(SQLConstants.API_RESOURCE_IDENTIFIER_COLUMN_NAME)) + .description(rs.getString(SQLConstants.API_RESOURCE_DESCRIPTION_COLUMN_NAME)) + .type(rs.getString(SQLConstants.API_RESOURCE_TYPE_COLUMN_NAME)) + .requiresAuthorization(rs.getBoolean(SQLConstants.REQUIRES_AUTHORIZATION_COLUMN_NAME)) + .tenantId(rs.getInt(SQLConstants.API_RESOURCE_TENANT_ID_COLUMN_NAME)) + .properties(apiResourceProperties); + apiResourceMap.put(apiResourceId, apiResourceBuilder.build()); + } + String propertyId = rs.getString(SQLConstants.API_RESOURCE_PROPERTY_ID_COLUMN_NAME); + if (StringUtils.isNotBlank(propertyId)) { + APIResourceProperty apiResourceProperty = new APIResourceProperty(); + apiResourceProperty.setName(rs.getString(SQLConstants.API_RESOURCE_PROPERTY_NAME_COLUMN_NAME)); + apiResourceProperty.setValue( + rs.getString(SQLConstants.API_RESOURCE_PROPERTY_VALUE_COLUMN_NAME)); + apiResourceMap.get(apiResourceId).getProperties().add(apiResourceProperty); + } + } + } catch (SQLException e) { + throw APIResourceManagementUtil.handleServerException( + APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_RETRIEVING_API_RESOURCES, e); + } + return new ArrayList<>(apiResourceMap.values()); + } + /** * Get API resource from the result set. * @@ -548,7 +635,8 @@ private List getAPIResourcesList(Integer limit, Integer tenantId, S * @return API resource. * @throws SQLException If an error occurs while retrieving API resource. */ - private static APIResource getApiResource(ResultSet resultSet) throws SQLException { + private static APIResource getApiResource(ResultSet resultSet, List apiResourceProperties) + throws SQLException { List scopes = new ArrayList<>(); APIResource apiResource = null; @@ -562,15 +650,18 @@ private static APIResource getApiResource(ResultSet resultSet) throws SQLExcepti .type(resultSet.getString(SQLConstants.API_RESOURCE_TYPE_COLUMN_NAME)) .requiresAuthorization(resultSet.getBoolean( SQLConstants.REQUIRES_AUTHORIZATION_COLUMN_NAME)) - .tenantId(resultSet.getInt(SQLConstants.API_RESOURCE_TENANT_ID_COLUMN_NAME)); + .tenantId(resultSet.getInt(SQLConstants.API_RESOURCE_TENANT_ID_COLUMN_NAME)) + .properties(apiResourceProperties); apiResource = apiResourceBuilder.build(); } - Scope.ScopeBuilder scopeBuilder = new Scope.ScopeBuilder() - .id(resultSet.getString(SQLConstants.SCOPE_ID_COLUMN_NAME)) - .name(resultSet.getString(SQLConstants.SCOPE_QUALIFIED_NAME_COLUMN_NAME)) - .displayName(resultSet.getString(SQLConstants.SCOPE_DISPLAY_NAME_COLUMN_NAME)) - .description(resultSet.getString(SQLConstants.SCOPE_DESCRIPTION_COLUMN_NAME)); - scopes.add(scopeBuilder.build()); + if (resultSet.getString(SQLConstants.SCOPE_ID_COLUMN_NAME) != null) { + Scope.ScopeBuilder scopeBuilder = new Scope.ScopeBuilder() + .id(resultSet.getString(SQLConstants.SCOPE_ID_COLUMN_NAME)) + .name(resultSet.getString(SQLConstants.SCOPE_QUALIFIED_NAME_COLUMN_NAME)) + .displayName(resultSet.getString(SQLConstants.SCOPE_DISPLAY_NAME_COLUMN_NAME)) + .description(resultSet.getString(SQLConstants.SCOPE_DESCRIPTION_COLUMN_NAME)); + scopes.add(scopeBuilder.build()); + } } if (apiResource != null) { apiResource.setScopes(scopes); @@ -588,7 +679,7 @@ private static APIResource getApiResource(ResultSet resultSet) throws SQLExcepti * @param limit Limit. * @return SQL statement to retrieve API resources. */ - private String buildAPIResourcesSqlStatement(String databaseName, Integer tenantId, String filterQuery, + private String buildGetAPIResourcesSqlStatement(String databaseName, Integer tenantId, String filterQuery, String sortOrder, Integer limit) { String sqlStmtHead = SQLConstants.GET_API_RESOURCES; @@ -604,6 +695,19 @@ private String buildAPIResourcesSqlStatement(String databaseName, Integer tenant return sqlStmtHead + filterQuery + String.format(sqlStmtTail, tenantId, sortOrder, limit); } + private String buildGetAPIResourcesWithPropertiesSqlStatement(String databaseName, Integer tenantId, + String filterQuery, String sortOrder, Integer limit) { + + String selectionQuery = databaseName.contains(SQLConstants.H2) + ? SQLConstants.GET_API_RESOURCES_WITH_PROPERTIES_SELECTION_H2 + : SQLConstants.GET_API_RESOURCES_WITH_PROPERTIES_SELECTION; + String getAPIResourcesSqlStmt = buildGetAPIResourcesSqlStatement(databaseName, tenantId, filterQuery, sortOrder, + limit); + String joinQuery = SQLConstants.GET_API_RESOURCES_WITH_PROPERTIES_JOIN; + + return selectionQuery + getAPIResourcesSqlStmt + String.format(joinQuery, sortOrder); + } + /** * Delete scopes by name. * @@ -699,6 +803,108 @@ private void addScopes(Connection dbConnection, String apiId, List scopes } } + /** + * Get API resource properties by API ID. + * + * @param dbConnection Database connection. + * @param apiId API resource id. + * @return List of API resource properties. + * @throws APIResourceMgtException If an error occurs while retrieving API resource properties. + */ + private List getAPIResourcePropertiesByAPIId(Connection dbConnection, String apiId) + throws APIResourceMgtException { + + List properties = new ArrayList<>(); + try { + String databaseName = dbConnection.getMetaData().getDatabaseProductName(); + PreparedStatement prepStmt = databaseName.contains(SQLConstants.H2) ? + dbConnection.prepareStatement(SQLConstants.GET_API_RESOURCE_PROPERTIES_BY_API_ID_H2) : + dbConnection.prepareStatement(SQLConstants.GET_API_RESOURCE_PROPERTIES_BY_API_ID); + prepStmt.setString(1, apiId); + try (ResultSet rs = prepStmt.executeQuery()) { + while (rs.next()) { + APIResourceProperty property = new APIResourceProperty(); + property.setName(rs.getString(SQLConstants.NAME_COLUMN_NAME)); + property.setValue(rs.getString(SQLConstants.VALUE_COLUMN_NAME)); + properties.add(property); + } + } + } catch (SQLException e) { + throw APIResourceManagementUtil.handleServerException( + APIResourceManagementConstants.ErrorMessages + .ERROR_CODE_ERROR_WHILE_RETRIEVING_API_RESOURCE_PROPERTIES, e); + } + return properties; + } + + /** + * Get API resource properties by API Identifier. + * + * @param dbConnection Database connection. + * @param apiIdentifier API resource id. + * @return List of API resource properties. + * @throws APIResourceMgtException If an error occurs while retrieving API resource properties. + */ + private List getAPIResourcePropertiesByAPIIdentifier( + Connection dbConnection, String apiIdentifier) throws APIResourceMgtException { + + List properties = new ArrayList<>(); + try { + String databaseName = dbConnection.getMetaData().getDatabaseProductName(); + PreparedStatement prepStmt = databaseName.contains(SQLConstants.H2) ? + dbConnection.prepareStatement(SQLConstants.GET_API_RESOURCE_PROPERTIES_BY_API_IDENTIFIER_H2) : + dbConnection.prepareStatement(SQLConstants.GET_API_RESOURCE_PROPERTIES_BY_API_IDENTIFIER); + prepStmt.setString(1, apiIdentifier); + try (ResultSet rs = prepStmt.executeQuery()) { + while (rs.next()) { + APIResourceProperty property = new APIResourceProperty(); + property.setName(rs.getString(SQLConstants.NAME_COLUMN_NAME)); + property.setValue(rs.getString(SQLConstants.VALUE_COLUMN_NAME)); + properties.add(property); + } + } + } catch (SQLException e) { + throw APIResourceManagementUtil.handleServerException( + APIResourceManagementConstants.ErrorMessages + .ERROR_CODE_ERROR_WHILE_RETRIEVING_API_RESOURCE_PROPERTIES, e); + } + return properties; + } + + /** + * Add API resource properties. + * + * @param dbConnection Database connection. + * @param apiId API resource id. + * @param properties List of API resource properties. + * @throws APIResourceMgtException If an error occurs while adding API resource properties. + */ + private void addAPIResourceProperties(Connection dbConnection, String apiId, List properties) + throws APIResourceMgtException { + + if (CollectionUtils.isEmpty(properties)) { + return; + } + + try { + String databaseName = dbConnection.getMetaData().getDatabaseProductName(); + String query = databaseName.contains(SQLConstants.H2) ? SQLConstants.ADD_API_RESOURCE_PROPERTY_H2 : + SQLConstants.ADD_API_RESOURCE_PROPERTY; + PreparedStatement prepStmt = dbConnection.prepareStatement(query); + for (APIResourceProperty property : properties) { + prepStmt.setString(1, apiId); + prepStmt.setString(2, property.getName()); + prepStmt.setString(3, property.getValue()); + prepStmt.addBatch(); + } + prepStmt.executeBatch(); + } catch (SQLException e) { + throw APIResourceManagementUtil.handleServerException( + APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_ADDING_API_RESOURCE_PROPERTIES, + e); + } + } + /** * Append the filter query to the query builder. * diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/dao/impl/CacheBackedAPIResourceMgtDAO.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/dao/impl/CacheBackedAPIResourceMgtDAO.java index 8104619a3bed..a4880548a940 100644 --- a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/dao/impl/CacheBackedAPIResourceMgtDAO.java +++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/dao/impl/CacheBackedAPIResourceMgtDAO.java @@ -61,6 +61,16 @@ public List getAPIResources(Integer limit, Integer tenantId, String return apiResourceManagementDAO.getAPIResources(limit, tenantId, sortOrder, expressionNodes); } + @Override + public List getAPIResourcesWithRequiredAttributes(Integer limit, Integer tenantId, String sortOrder, + List expressionNodes, + List requiredAttributes) + throws APIResourceMgtException { + + return apiResourceManagementDAO.getAPIResourcesWithRequiredAttributes(limit, tenantId, sortOrder, + expressionNodes, requiredAttributes); + } + @Override public Integer getAPIResourcesCount(Integer tenantId, List expressionNodes) throws APIResourceMgtException { diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/dbscripts/h2.sql b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/dbscripts/h2.sql index 524aee645ebc..a502a2047bfa 100644 --- a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/dbscripts/h2.sql +++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/dbscripts/h2.sql @@ -1273,6 +1273,16 @@ CREATE TABLE IF NOT EXISTS API_RESOURCE ( CONSTRAINT IDENTIFIER_UNIQUE UNIQUE (IDENTIFIER, TENANT_ID) ); +CREATE TABLE IF NOT EXISTS API_RESOURCE_PROPERTY ( + ID INTEGER AUTO_INCREMENT, + API_ID CHAR(36) NOT NULL, + NAME VARCHAR(255) NOT NULL, + `VALUE` VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT API_RESOURCE_PROPERTY_CONSTRAINT UNIQUE (API_ID, NAME), + FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE +); + CREATE TABLE IF NOT EXISTS SCOPE ( ID CHAR(36) NOT NULL PRIMARY KEY, CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT, diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/APIResource.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/APIResource.java index e8a92da730b4..9053f7cc4d98 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/APIResource.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/APIResource.java @@ -35,6 +35,7 @@ public class APIResource { private boolean requiresAuthorization; private List scopes; private List subscribedApplications; + private List properties; public APIResource() { } @@ -51,6 +52,7 @@ public APIResource(APIResourceBuilder apiResourceBuilder) { this.requiresAuthorization = apiResourceBuilder.requiresAuthorization; this.scopes = apiResourceBuilder.scopes; this.subscribedApplications = apiResourceBuilder.subscribedApplications; + this.properties = apiResourceBuilder.properties; } public String getId() { @@ -113,6 +115,16 @@ public void setSubscribedApplications(List subscribedAppli this.subscribedApplications = subscribedApplications; } + public List getProperties() { + + return properties; + } + + public void setProperties(List properties) { + + this.properties = properties; + } + /** * API resource builder. */ @@ -128,6 +140,7 @@ public static class APIResourceBuilder { private boolean requiresAuthorization; private List scopes; private List subscribedApplications; + private List properties; public APIResourceBuilder() { } @@ -192,6 +205,12 @@ public APIResourceBuilder subscribedApplications(List subs return this; } + public APIResourceBuilder properties(List properties) { + + this.properties = properties; + return this; + } + public APIResource build() { return new APIResource(this); diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/APIResourceProperty.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/APIResourceProperty.java new file mode 100644 index 000000000000..4316ea819db8 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/APIResourceProperty.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.application.common.model; + +/** + * API Resource property. + */ +public class APIResourceProperty { + + private String name; + private String value; + + public String getName() { + + return name; + } + + public void setName(String name) { + + this.name = name; + } + + public String getValue() { + + return value; + } + + public void setValue(String value) { + + this.value = value; + } +} diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql index c89855e9e42f..b28b4308ecb7 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql @@ -1971,6 +1971,27 @@ CREATE TRIGGER API_RESOURCE_TRIG NO CASCADE END / +CREATE TABLE API_RESOURCE_PROPERTY ( + ID INTEGER NOT NULL, + API_ID CHAR(36) NOT NULL, + NAME VARCHAR(255) NOT NULL, + VALUE VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT API_RESOURCE_PROPERTY_CONSTRAINT UNIQUE (API_ID, NAME), + FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE) +/ +CREATE SEQUENCE API_RESOURCE_PROPERTY_SEQ START WITH 1 INCREMENT BY 1 NOCACHE +/ +CREATE TRIGGER API_RESOURCE_PROPERTY_TRIG NO CASCADE + BEFORE INSERT + ON API_RESOURCE_PROPERTY + REFERENCING NEW AS NEW + FOR EACH ROW MODE DB2SQL + BEGIN ATOMIC + SET (NEW.ID) = (NEXTVAL FOR API_RESOURCE_PROPERTY_SEQ); + END +/ + CREATE TABLE SCOPE ( ID CHAR(36) NOT NULL PRIMARY KEY, CURSOR_KEY INTEGER NOT NULL, diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql index c69b970df86f..4886e179a206 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql @@ -1321,6 +1321,16 @@ CREATE TABLE IF NOT EXISTS API_RESOURCE ( CONSTRAINT IDENTIFIER_UNIQUE UNIQUE (IDENTIFIER, TENANT_ID) ); +CREATE TABLE IF NOT EXISTS API_RESOURCE_PROPERTY ( + ID INTEGER AUTO_INCREMENT, + API_ID CHAR(36) NOT NULL, + NAME VARCHAR(255) NOT NULL, + `VALUE` VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT API_RESOURCE_PROPERTY_CONSTRAINT UNIQUE (API_ID, NAME), + FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE +); + CREATE TABLE IF NOT EXISTS SCOPE ( ID CHAR(36) NOT NULL PRIMARY KEY, CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT, diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql index a8a15f48971a..f37679cdae60 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql @@ -1464,6 +1464,17 @@ CREATE TABLE API_RESOURCE ( CONSTRAINT IDENTIFIER_UNIQUE UNIQUE (IDENTIFIER, TENANT_ID) ); +IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[API_RESOURCE_PROPERTY]') AND TYPE IN (N'U')) +CREATE TABLE API_RESOURCE_PROPERTY ( + ID INTEGER IDENTITY, + API_ID CHAR(36) NOT NULL, + NAME VARCHAR(255) NOT NULL, + VALUE VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT API_RESOURCE_PROPERTY_CONSTRAINT UNIQUE (API_ID, NAME), + FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE +); + IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[SCOPE]') AND TYPE IN (N'U')) CREATE TABLE SCOPE ( ID CHAR(36) NOT NULL PRIMARY KEY, diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql index d71990943082..9953f7659421 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql @@ -1492,6 +1492,17 @@ CREATE TABLE IF NOT EXISTS API_RESOURCE ( CONSTRAINT IDENTIFIER_UNIQUE UNIQUE (IDENTIFIER, TENANT_ID) )ENGINE NDB; +CREATE TABLE IF NOT EXISTS API_RESOURCE_PROPERTY ( + ID INTEGER AUTO_INCREMENT, + API_ID CHAR(36) NOT NULL, + NAME VARCHAR(255) NOT NULL, + VALUE VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT API_RESOURCE_PROPERTY_CONSTRAINT UNIQUE (API_ID, NAME), + FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) + ON DELETE CASCADE +)ENGINE NDB; + CREATE TABLE IF NOT EXISTS SCOPE ( ID CHAR(36) NOT NULL PRIMARY KEY, CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT, diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql index 280dd8ccf5df..c699823a0ec1 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql @@ -1350,6 +1350,16 @@ CREATE TABLE IF NOT EXISTS API_RESOURCE ( CONSTRAINT IDENTIFIER_UNIQUE UNIQUE (IDENTIFIER, TENANT_ID) )DEFAULT CHARACTER SET latin1 ENGINE INNODB; +CREATE TABLE IF NOT EXISTS API_RESOURCE_PROPERTY ( + ID INTEGER AUTO_INCREMENT, + API_ID CHAR(36) NOT NULL, + NAME VARCHAR(255) NOT NULL, + VALUE VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT API_RESOURCE_PROPERTY_CONSTRAINT UNIQUE (API_ID, NAME), + FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE +)DEFAULT CHARACTER SET latin1 ENGINE INNODB; + CREATE TABLE IF NOT EXISTS SCOPE ( ID CHAR(36) NOT NULL PRIMARY KEY, CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT, diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql index 1d09e0d839f6..f0caf1f3e287 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql @@ -1570,6 +1570,19 @@ CREATE TABLE API_RESOURCE ( CONSTRAINT IDENTIFIER_UNIQUE UNIQUE (IDENTIFIER, TENANT_ID) ); +DROP TABLE IF EXISTS API_RESOURCE_PROPERTY; +DROP SEQUENCE IF EXISTS API_RESOURCE_PROPERTY_SEQ; +CREATE SEQUENCE API_RESOURCE_PROPERTY_SEQ; +CREATE TABLE API_RESOURCE_PROPERTY ( + ID INTEGER DEFAULT NEXTVAL('API_RESOURCE_PROPERTY_SEQ'), + API_ID CHAR(36) NOT NULL, + NAME VARCHAR(255) NOT NULL, + VALUE VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT API_RESOURCE_PROPERTY_CONSTRAINT UNIQUE (API_ID, NAME), + FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE +); + DROP TABLE IF EXISTS SCOPE; DROP SEQUENCE IF EXISTS SCOPE_SEQ; CREATE SEQUENCE SCOPE_SEQ;