diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/pom.xml b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/pom.xml
new file mode 100644
index 000000000000..78b1e865a662
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/pom.xml
@@ -0,0 +1,204 @@
+
+
+
+ 4.0.0
+
+ org.wso2.carbon.identity.framework
+ api-resource-mgt
+ 5.25.319-SNAPSHOT
+ ../pom.xml
+
+ org.wso2.carbon.identity.api.resource.mgt
+ bundle
+ WSO2 Identity - API Resource Management Component
+ The API Resource Management backend component
+ http://wso2.org
+
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.application.common
+
+
+ org.apache.felix
+ org.apache.felix.scr.ds-annotations
+
+
+
+ org.jacoco
+ org.jacoco.agent
+ runtime
+ test
+
+
+ org.testng
+ testng
+ test
+
+
+ com.h2database
+ h2
+ test
+
+
+ org.powermock
+ powermock-api-mockito2
+ test
+
+
+ org.powermock
+ powermock-module-testng
+ test
+
+
+ org.powermock
+ powermock-module-testng-common
+ test
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.testutil
+ test
+
+
+
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ true
+
+
+
+ ${project.artifactId}
+
+ ${project.artifactId}
+
+ org.wso2.carbon.identity.api.resource.mgt.internal,
+
+
+ !org.wso2.carbon.identity.api.resource.mgt.internal,
+ org.wso2.carbon.identity.api.resource.mgt.*; version="${carbon.identity.package.export.version}"
+
+
+ org.osgi.framework;version="${osgi.framework.imp.pkg.version.range}",
+ org.osgi.service.component; version="${osgi.service.component.imp.pkg.version.range}",
+ javax.sql,
+ org.apache.commons.collections; version="${commons-collections.wso2.osgi.version.range}",
+ org.apache.commons.lang; version="${commons-lang.wso2.osgi.version.range}",
+ org.apache.commons.logging; version="${import.package.version.commons.logging}",
+ org.wso2.carbon.context; version="${carbon.kernel.package.import.version.range}",
+ org.wso2.carbon.database.utils.jdbc;
+ version="${org.wso2.carbon.database.utils.version.range}",
+ org.wso2.carbon.database.utils.jdbc.exceptions;
+ version="${org.wso2.carbon.database.utils.version.range}",
+ org.wso2.carbon.identity.application.common.model;
+ version="${carbon.identity.package.import.version.range}",
+ org.wso2.carbon.identity.base; version="${carbon.identity.package.import.version.range}",
+ org.wso2.carbon.identity.core.cache;
+ version="${carbon.identity.package.import.version.range}",
+ org.wso2.carbon.identity.core.model;
+ version="${carbon.identity.package.import.version.range}",
+ org.wso2.carbon.identity.core.util;
+ version="${carbon.identity.package.import.version.range}",
+ org.wso2.carbon.user.api; version="${carbon.user.api.imp.pkg.version.range}",
+ org.wso2.carbon.user.core.util; version="${carbon.kernel.package.import.version.range}",
+ org.wso2.carbon.utils; version="${carbon.kernel.package.import.version.range}",
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven.surefire.plugin.version}
+
+
+ --add-opens java.xml/jdk.xml.internal=ALL-UNNAMED
+ --add-exports java.base/jdk.internal.loader=ALL-UNNAMED
+
+
+ src/test/resources/testng.xml
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco.version}
+
+
+ default-prepare-agent
+
+ prepare-agent
+
+
+
+ default-prepare-agent-integration
+
+ prepare-agent-integration
+
+
+
+ default-report
+
+ report
+
+
+
+ default-report-integration
+
+ report-integration
+
+
+
+ default-check
+
+ check
+
+
+
+
+ BUNDLE
+
+
+ COMPLEXITY
+ COVEREDRATIO
+ 0.90
+
+
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ findbugs-maven-plugin
+
+ High
+
+
+
+
+
+
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
new file mode 100644
index 000000000000..fbdc5e976d25
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceManager.java
@@ -0,0 +1,149 @@
+/*
+ * 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.api.resource.mgt;
+
+import org.wso2.carbon.identity.api.resource.mgt.model.APIResourceSearchResult;
+import org.wso2.carbon.identity.application.common.model.APIResource;
+import org.wso2.carbon.identity.application.common.model.Scope;
+
+import java.util.List;
+
+/**
+ * API Resource Manager Interface.
+ */
+public interface APIResourceManager {
+
+ /**
+ * Get API resources.
+ *
+ * @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.
+ * @return API resource search result.
+ * @throws APIResourceMgtException If an error occurs while retrieving API resources.
+ */
+ APIResourceSearchResult getAPIResources(String after, String before, Integer limit, String filter, String sortOrder,
+ String tenantDomain)
+ throws APIResourceMgtException;
+
+ /**
+ * Get API resource by id.
+ *
+ * @param apiResourceId API resource id.
+ * @param tenantDomain Tenant domain.
+ * @return An APIResource
.
+ * @throws APIResourceMgtException If an error occurs while retrieving the API resource.
+ */
+ APIResource getAPIResourceById(String apiResourceId, String tenantDomain) throws APIResourceMgtException;
+
+ /**
+ * Add API resource.
+ *
+ * @param apiResource API resource.
+ * @param tenantDomain Tenant domain.
+ * @return An APIResource
.
+ * @throws APIResourceMgtException If an error occurs while adding the API resource.
+ */
+ APIResource addAPIResource(APIResource apiResource, String tenantDomain) throws APIResourceMgtException;
+
+ /**
+ * Delete API resource by id.
+ *
+ * @param apiResourceId API resource id.
+ * @param tenantDomain Tenant domain.
+ * @throws APIResourceMgtException If an error occurs while deleting the API resource.
+ */
+ void deleteAPIResourceById(String apiResourceId, String tenantDomain) throws APIResourceMgtException;
+
+ /**
+ * Update API resource.
+ *
+ * @param apiResource API resource.
+ * @param tenantDomain Tenant domain.
+ * @throws APIResourceMgtException If an error occurs while updating the API resource.
+ */
+ void updateAPIResource(APIResource apiResource, List addedScopes, List removedScopes,
+ String tenantDomain) throws APIResourceMgtException;
+
+ /**
+ * Get API resource by identifier.
+ *
+ * @param apiResourceIdentifier API resource identifier.
+ * @param tenantDomain Tenant domain.
+ * @return An APIResource
.
+ * @throws APIResourceMgtException If an error occurs while retrieving the API resource.
+ */
+ APIResource getAPIResourceByIdentifier(String apiResourceIdentifier, String tenantDomain)
+ throws APIResourceMgtException;
+
+ /**
+ * Get API scopes by id.
+ *
+ * @param apiResourceId API resource id.
+ * @param tenantDomain Tenant domain.
+ * @return List of Scope
.
+ * @throws APIResourceMgtException If an error occurs while retrieving API scopes.
+ */
+ List getAPIScopesById(String apiResourceId, String tenantDomain) throws APIResourceMgtException;
+
+ /**
+ * Delete API scopes by id.
+ *
+ * @param apiResourceId API resource id.
+ * @param tenantDomain Tenant domain.
+ * @throws APIResourceMgtException If an error occurs while deleting API scopes.
+ */
+ void deleteAPIScopesById(String apiResourceId, String tenantDomain) throws APIResourceMgtException;
+
+ /**
+ * Delete API scope by scope id.
+ *
+ * @param apiResourceId API resource id.
+ * @param scopeName Scope id.
+ * @param tenantDomain Tenant domain.
+ * @throws APIResourceMgtException If an error occurs while deleting API scope.
+ */
+ void deleteAPIScopeByScopeName(String apiResourceId, String scopeName, String tenantDomain)
+ throws APIResourceMgtException;
+
+ /**
+ * Put scopes to API resource.
+ *
+ * @param apiResourceId API resource id.
+ * @param currentScopes List of Scope
in API resource.
+ * @param scopes List of Scope
.
+ * @param tenantDomain Tenant domain.
+ * @throws APIResourceMgtException If an error occurs while putting scopes to API resource.
+ */
+ void putScopes(String apiResourceId, List currentScopes, List scopes, String tenantDomain)
+ throws APIResourceMgtException;
+
+ /**
+ * Get scopes by tenant domain.
+ *
+ * @param tenantDomain Tenant domain.
+ * @param filter Filter expression.
+ * @return List of Scope
.
+ * @throws APIResourceMgtException If an error occurs while retrieving scopes.
+ */
+ List getScopesByTenantDomain(String tenantDomain, String filter) 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/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
new file mode 100644
index 000000000000..8b7ffb83dcd3
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceManagerImpl.java
@@ -0,0 +1,220 @@
+/*
+ * 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.api.resource.mgt;
+
+import org.apache.commons.lang.StringUtils;
+import org.wso2.carbon.identity.api.resource.mgt.constant.APIResourceManagementConstants;
+import org.wso2.carbon.identity.api.resource.mgt.dao.impl.APIResourceManagementDAOImpl;
+import org.wso2.carbon.identity.api.resource.mgt.dao.impl.CacheBackedAPIResourceMgtDAO;
+import org.wso2.carbon.identity.api.resource.mgt.model.APIResourceSearchResult;
+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.Scope;
+import org.wso2.carbon.identity.base.IdentityException;
+import org.wso2.carbon.identity.core.model.ExpressionNode;
+import org.wso2.carbon.identity.core.model.FilterTreeBuilder;
+import org.wso2.carbon.identity.core.model.Node;
+import org.wso2.carbon.identity.core.model.OperationNode;
+import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+
+/**
+ * API resource management service.
+ */
+public class APIResourceManagerImpl implements APIResourceManager {
+
+ private static final APIResourceManager INSTANCE = new APIResourceManagerImpl();
+
+ private static final CacheBackedAPIResourceMgtDAO CACHE_BACKED_DAO =
+ new CacheBackedAPIResourceMgtDAO(new APIResourceManagementDAOImpl());
+
+ private APIResourceManagerImpl() {
+
+ }
+
+ public static APIResourceManager getInstance() {
+
+ return INSTANCE;
+ }
+
+ @Override
+ public APIResourceSearchResult getAPIResources(String after, String before, Integer limit, String filter,
+ String sortOrder, String tenantDomain)
+ 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.getAPIResources(limit, tenantId, sortOrder, expressionNodes));
+ return result;
+ }
+
+ @Override
+ public APIResource getAPIResourceById(String apiResourceId, String tenantDomain)
+ throws APIResourceMgtException {
+
+ return CACHE_BACKED_DAO.getAPIResourceById(apiResourceId, IdentityTenantUtil.getTenantId(tenantDomain));
+ }
+
+ @Override
+ public APIResource addAPIResource(APIResource apiResource, String tenantDomain)
+ throws APIResourceMgtException {
+
+ return CACHE_BACKED_DAO.addAPIResource(apiResource, IdentityTenantUtil.getTenantId(tenantDomain));
+ }
+
+ @Override
+ public void deleteAPIResourceById(String apiResourceId, String tenantDomain) throws APIResourceMgtException {
+
+ CACHE_BACKED_DAO.deleteAPIResourceById(apiResourceId, IdentityTenantUtil.getTenantId(tenantDomain));
+ }
+
+ @Override
+ public void updateAPIResource(APIResource apiResource, List addedScopes, List removedScopes,
+ String tenantDomain) throws APIResourceMgtException {
+
+ CACHE_BACKED_DAO.updateAPIResource(apiResource, addedScopes, removedScopes,
+ IdentityTenantUtil.getTenantId(tenantDomain));
+ }
+
+ @Override
+ public APIResource getAPIResourceByIdentifier(String apiResourceIdentifier, String tenantDomain)
+ throws APIResourceMgtException {
+
+ return CACHE_BACKED_DAO.getAPIResourceByIdentifier(apiResourceIdentifier,
+ IdentityTenantUtil.getTenantId(tenantDomain));
+ }
+
+ @Override
+ public List getAPIScopesById(String apiResourceId, String tenantDomain)
+ throws APIResourceMgtException {
+
+ return CACHE_BACKED_DAO.getScopesByAPI(apiResourceId, IdentityTenantUtil.getTenantId(tenantDomain));
+ }
+
+ @Override
+ public void deleteAPIScopesById(String apiResourceId, String tenantDomain) throws APIResourceMgtException {
+
+ CACHE_BACKED_DAO.deleteAllScopes(apiResourceId, IdentityTenantUtil.getTenantId(tenantDomain));
+ }
+
+ @Override
+ public void deleteAPIScopeByScopeName(String apiResourceId, String scopeName, String tenantDomain)
+ throws APIResourceMgtException {
+
+ CACHE_BACKED_DAO.deleteScope(apiResourceId, scopeName, IdentityTenantUtil.getTenantId(tenantDomain));
+ }
+
+ @Override
+ public void putScopes(String apiResourceId, List currentScopes, List scopes, String tenantDomain)
+ throws APIResourceMgtException {
+
+ CACHE_BACKED_DAO.putScopes(apiResourceId, currentScopes, scopes, IdentityTenantUtil.getTenantId(tenantDomain));
+ }
+
+ @Override
+ public List getScopesByTenantDomain(String tenantDomain, String filter) throws APIResourceMgtException {
+
+ List expressionNodes = getExpressionNodes(filter, null, null);
+ int tenantId = IdentityTenantUtil.getTenantId(tenantDomain);
+ return CACHE_BACKED_DAO.getScopesByTenantId(tenantId, expressionNodes);
+ }
+
+ /**
+ * Get the filter node as a list.
+ *
+ * @param filter Filter string.
+ * @param after After cursor.
+ * @param before Before cursor.
+ * @throws APIResourceMgtClientException Error when validate filters.
+ */
+ private List getExpressionNodes(String filter, String after, String before)
+ throws APIResourceMgtClientException {
+
+ List expressionNodes = new ArrayList<>();
+ filter = StringUtils.isBlank(filter) ? StringUtils.EMPTY : filter;
+ String paginatedFilter = this.getPaginatedFilter(filter, after, before);
+ try {
+ if (StringUtils.isNotBlank(paginatedFilter)) {
+ FilterTreeBuilder filterTreeBuilder = new FilterTreeBuilder(paginatedFilter);
+ Node rootNode = filterTreeBuilder.buildTree();
+ this.setExpressionNodeList(rootNode, expressionNodes);
+ }
+ return expressionNodes;
+ } catch (IOException | IdentityException e) {
+ throw APIResourceManagementUtil.handleClientException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_INVALID_FILTER_FORMAT);
+ }
+ }
+
+ /**
+ * Get pagination filter.
+ *
+ * @param paginatedFilter Filter string.
+ * @param after After cursor.
+ * @param before Before cursor.
+ * @return Filter string.
+ * @throws APIResourceMgtClientException Error when validate filters.
+ */
+ private String getPaginatedFilter(String paginatedFilter, String after, String before) throws
+ APIResourceMgtClientException {
+
+ try {
+ if (StringUtils.isNotBlank(before)) {
+ String decodedString = new String(Base64.getDecoder().decode(before), StandardCharsets.UTF_8);
+ paginatedFilter += StringUtils.isNotBlank(paginatedFilter) ? " and " +
+ APIResourceManagementConstants.BEFORE_GT + decodedString :
+ APIResourceManagementConstants.BEFORE_GT + decodedString;
+ } else if (StringUtils.isNotBlank(after)) {
+ String decodedString = new String(Base64.getDecoder().decode(after), StandardCharsets.UTF_8);
+ paginatedFilter += StringUtils.isNotBlank(paginatedFilter) ? " and " +
+ APIResourceManagementConstants.AFTER_LT + decodedString :
+ APIResourceManagementConstants.AFTER_LT + decodedString;
+ }
+ } catch (IllegalArgumentException e) {
+ throw APIResourceManagementUtil.handleClientException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_INVALID_CURSOR_FOR_PAGINATION);
+ }
+ return paginatedFilter;
+ }
+
+ /**
+ * Set the node values as list of expression.
+ *
+ * @param node filter node.
+ * @param expression list of expression.
+ */
+ private void setExpressionNodeList(Node node, List expression) {
+
+ if (node instanceof ExpressionNode) {
+ if (StringUtils.isNotBlank(((ExpressionNode) node).getAttributeValue())) {
+ expression.add((ExpressionNode) node);
+ }
+ } else if (node instanceof OperationNode) {
+ setExpressionNodeList(node.getLeftNode(), expression);
+ setExpressionNodeList(node.getRightNode(), expression);
+ }
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceMgtClientException.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceMgtClientException.java
new file mode 100644
index 000000000000..83a4ecc1d5fa
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceMgtClientException.java
@@ -0,0 +1,30 @@
+/*
+ * 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.api.resource.mgt;
+
+/**
+ * API resource management client exception.
+ */
+public class APIResourceMgtClientException extends APIResourceMgtException {
+
+ public APIResourceMgtClientException(String message, String description, String errorCode) {
+
+ super(message, description, errorCode);
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceMgtException.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceMgtException.java
new file mode 100644
index 000000000000..f5cb8efe90a1
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceMgtException.java
@@ -0,0 +1,79 @@
+/*
+ * 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.api.resource.mgt;
+
+/**
+ * API resource management exception.
+ */
+public class APIResourceMgtException extends Exception {
+
+ private String errorCode;
+ private String description;
+
+ public APIResourceMgtException(String message) {
+
+ super(message);
+ }
+
+ public APIResourceMgtException(String message, String errorCode) {
+
+ super(message);
+ this.errorCode = errorCode;
+ }
+
+ public APIResourceMgtException(String message, String errorCode, Throwable cause) {
+
+ super(message, cause);
+ this.errorCode = errorCode;
+ }
+
+ public APIResourceMgtException(String message, String description, String errorCode) {
+
+ super(message);
+ this.errorCode = errorCode;
+ this.description = description;
+ }
+
+ public APIResourceMgtException(String message, String description, String errorCode, Throwable cause) {
+
+ super(message, cause);
+ this.errorCode = errorCode;
+ this.description = description;
+ }
+
+ public String getErrorCode() {
+
+ return this.errorCode;
+ }
+
+ public void setErrorCode(String errorCode) {
+
+ this.errorCode = errorCode;
+ }
+
+ public String getDescription() {
+
+ return this.description;
+ }
+
+ public void setDescription(String description) {
+
+ this.description = description;
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceMgtServerException.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceMgtServerException.java
new file mode 100644
index 000000000000..0a90a2c3dd71
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceMgtServerException.java
@@ -0,0 +1,46 @@
+/*
+ * 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.api.resource.mgt;
+
+/**
+ * API resource management server exception.
+ */
+public class APIResourceMgtServerException extends APIResourceMgtException {
+
+ public APIResourceMgtServerException(String message, String errorCode) {
+
+ super(message, errorCode);
+ }
+
+ public APIResourceMgtServerException(String message, String errorCode, Throwable cause) {
+
+ super(message, errorCode, cause);
+ }
+
+ public APIResourceMgtServerException(String message, String description, String errorCode,
+ Throwable cause) {
+
+ super(message, description, errorCode, cause);
+ }
+
+ public APIResourceMgtServerException(String message) {
+
+ super(message);
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceCacheById.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceCacheById.java
new file mode 100644
index 000000000000..b70861288311
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceCacheById.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.api.resource.mgt.cache;
+
+import org.wso2.carbon.identity.core.cache.BaseCache;
+import org.wso2.carbon.utils.CarbonUtils;
+
+/**
+ * Cache entry for API resource.
+ */
+public class APIResourceCacheById extends BaseCache {
+
+ private static final String CACHE_NAME = "APIResourceCacheById";
+
+ private static final APIResourceCacheById INSTANCE = new APIResourceCacheById();
+
+ private APIResourceCacheById() {
+
+ super(CACHE_NAME);
+ }
+
+ /**
+ * Get API resource cache by id instance.
+ *
+ * @return API resource cache by id instance.
+ */
+ public static APIResourceCacheById getInstance() {
+
+ CarbonUtils.checkSecurity();
+ return INSTANCE;
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceCacheByIdentifier.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceCacheByIdentifier.java
new file mode 100644
index 000000000000..b8c75af92c15
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceCacheByIdentifier.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.api.resource.mgt.cache;
+
+import org.wso2.carbon.identity.core.cache.BaseCache;
+import org.wso2.carbon.utils.CarbonUtils;
+
+/**
+ * Cache entry for API resource.
+ */
+public class APIResourceCacheByIdentifier extends BaseCache {
+
+ private static final String CACHE_NAME = "APIResourceCacheByIdentifier";
+
+ private static final APIResourceCacheByIdentifier INSTANCE = new APIResourceCacheByIdentifier();
+
+ private APIResourceCacheByIdentifier() {
+
+ super(CACHE_NAME);
+ }
+
+ /**
+ * Get API resource cache by id instance.
+ *
+ * @return API resource cache by id instance.
+ */
+ public static APIResourceCacheByIdentifier getInstance() {
+
+ CarbonUtils.checkSecurity();
+ return INSTANCE;
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceCacheEntry.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceCacheEntry.java
new file mode 100644
index 000000000000..4cec4939f434
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceCacheEntry.java
@@ -0,0 +1,45 @@
+/*
+ * 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.api.resource.mgt.cache;
+
+import org.wso2.carbon.identity.application.common.model.APIResource;
+import org.wso2.carbon.identity.core.cache.CacheEntry;
+
+/**
+ * Cache entry for API resource.
+ */
+public class APIResourceCacheEntry extends CacheEntry {
+
+ private APIResource apiResource;
+
+ public APIResourceCacheEntry(APIResource apiResource) {
+
+ this.apiResource = apiResource;
+ }
+
+ public APIResource getAPIResource() {
+
+ return apiResource;
+ }
+
+ public void setAPIResource(APIResource apiResource) {
+
+ this.apiResource = apiResource;
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceIdCacheKey.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceIdCacheKey.java
new file mode 100644
index 000000000000..68bbcdf02a2f
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceIdCacheKey.java
@@ -0,0 +1,54 @@
+/*
+ * 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.api.resource.mgt.cache;
+
+import org.wso2.carbon.identity.core.cache.CacheKey;
+
+/**
+ * Cache key for API resource.
+ */
+public class APIResourceIdCacheKey extends CacheKey {
+
+ private final String resourceId;
+
+ public APIResourceIdCacheKey(String resourceId) {
+
+ this.resourceId = resourceId;
+ }
+
+ public String getResourceId() {
+
+ return resourceId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+ if (!(o instanceof APIResourceIdCacheKey)) {
+ return false;
+ }
+ return resourceId.equals(((APIResourceIdCacheKey) o).getResourceId());
+ }
+
+ @Override
+ public int hashCode() {
+
+ return resourceId.hashCode();
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceIdentifierCacheKey.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceIdentifierCacheKey.java
new file mode 100644
index 000000000000..262049b693b5
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/cache/APIResourceIdentifierCacheKey.java
@@ -0,0 +1,54 @@
+/*
+ * 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.api.resource.mgt.cache;
+
+import org.wso2.carbon.identity.core.cache.CacheKey;
+
+/**
+ * Cache key for API resource name.
+ */
+public class APIResourceIdentifierCacheKey extends CacheKey {
+
+ private final String identifier;
+
+ public APIResourceIdentifierCacheKey(String identifier) {
+
+ this.identifier = identifier;
+ }
+
+ public String getIdentifier() {
+
+ return identifier;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+ if (!(o instanceof APIResourceIdentifierCacheKey)) {
+ return false;
+ }
+ return identifier.equals(((APIResourceIdentifierCacheKey) o).getIdentifier());
+ }
+
+ @Override
+ public int hashCode() {
+
+ return identifier.hashCode();
+ }
+}
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
new file mode 100644
index 000000000000..77a57d552b72
--- /dev/null
+++ 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
@@ -0,0 +1,127 @@
+/*
+ * 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.api.resource.mgt.constant;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * API resource management constants.
+ */
+public class APIResourceManagementConstants {
+
+ public static final String NAME = "name";
+ public static final String IDENTIFIER = "identifier";
+ public static final String BEFORE = "before";
+ public static final String AFTER = "after";
+ public static final String EQ = "eq";
+ public static final String CO = "co";
+ public static final String SW = "sw";
+ public static final String EW = "ew";
+ public static final String GE = "ge";
+ public static final String LE = "le";
+ public static final String GT = "gt";
+ public static final String LT = "lt";
+ public static final String BEFORE_GT = "before gt ";
+ public static final String AFTER_LT = "after lt ";
+ private static final Map attributeColumnMap = new HashMap<>();
+ private static final Map scopeAttributeColumnMap = new HashMap<>();
+ public static final Map ATTRIBUTE_COLUMN_MAP = Collections.unmodifiableMap(attributeColumnMap);
+ public static final Map SCOPE_ATTRIBUTE_COLUMN_MAP =
+ Collections.unmodifiableMap(scopeAttributeColumnMap);
+
+ static {
+ attributeColumnMap.put(NAME, SQLConstants.NAME_COLUMN_NAME);
+ attributeColumnMap.put(IDENTIFIER, SQLConstants.IDENTIFIER_COLUMN_NAME);
+ attributeColumnMap.put(BEFORE, SQLConstants.CURSOR_KEY_COLUMN_NAME);
+ attributeColumnMap.put(AFTER, SQLConstants.CURSOR_KEY_COLUMN_NAME);
+
+ scopeAttributeColumnMap.put(NAME, SQLConstants.NAME_COLUMN_NAME);
+ }
+
+ /**
+ * Error messages.
+ */
+ public enum ErrorMessages {
+
+ // Client errors.
+ ERROR_CODE_INVALID_FILTER_FORMAT("60001", "Unable to retrieve API resources.",
+ "Invalid format used for filtering."),
+ ERROR_CODE_INVALID_CURSOR_FOR_PAGINATION("60002", "Unable to retrieve tenant domains.",
+ "Invalid cursor used for pagination."),
+ ERROR_CODE_API_RESOURCE_ALREADY_EXISTS("60003", "Unable to add API resource.",
+ "API resource already exists for the tenant: %s."),
+ ERROR_CODE_SCOPE_ALREADY_EXISTS("60004", "Unable to add scope.",
+ "Scope already exists for the tenant: %s."),
+ ERROR_CODE_INVALID_FILTER_VALUE("60005", "Unable to retrieve API resources.",
+ "Invalid filter value used for filtering."),
+
+ // Server errors.
+ ERROR_CODE_ERROR_WHILE_RETRIEVING_API_RESOURCES("65001", "Error while retrieving API resources.",
+ "Error while retrieving API resources from the database."),
+ ERROR_CODE_ERROR_WHILE_RETRIEVING_SCOPES("65002", "Error while retrieving scopes.",
+ "Error while retrieving scopes from the database."),
+ ERROR_CODE_ERROR_WHILE_ADDING_API_RESOURCE("65003", "Error while adding API resource.",
+ "Error while adding API resource to the database."),
+ ERROR_CODE_ERROR_WHILE_DELETING_API_RESOURCE("65004", "Error while deleting API resource.",
+ "Error while deleting API resource from the database."),
+ ERROR_CODE_ERROR_WHILE_UPDATING_API_RESOURCE("65005", "Error while updating API resource.",
+ "Error while updating API resource in the database."),
+ ERROR_CODE_ERROR_WHILE_ADDING_SCOPES("65006", "Error while adding scopes.",
+ "Error while adding scopes to the database."),
+ ERROR_CODE_ERROR_WHILE_UPDATING_SCOPES("65007", "Error while updating scopes.",
+ "Error while updating scopes in the database."),
+ ERROR_CODE_ERROR_WHILE_DELETING_SCOPES("65008", "Error while deleting scopes.",
+ "Error while deleting scopes from the database."),
+ ERROR_CODE_ERROR_WHILE_GETTING_SCOPES("65009", "Error while getting scopes.",
+ "Error while getting scopes from the database."),
+ ERROR_CODE_ERROR_WHILE_CHECKING_EXISTENCE_OF_SCOPE("65010", "Error while checking existence of" +
+ " 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."),
+ ;
+
+ private final String code;
+ private final String message;
+ private final String description;
+
+ ErrorMessages(String code, String message, String description) {
+
+ this.code = code;
+ this.message = message;
+ this.description = description;
+ }
+
+ public String getCode() {
+
+ return code;
+ }
+
+ public String getMessage() {
+
+ return message;
+ }
+
+ public String getDescription() {
+
+ return description;
+ }
+ }
+}
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
new file mode 100644
index 000000000000..9472d6c43d6a
--- /dev/null
+++ 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
@@ -0,0 +1,114 @@
+/*
+ * 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.api.resource.mgt.constant;
+
+/**
+ * SQL constants for API resource management service.
+ */
+public class SQLConstants {
+
+ // DB types.
+ public static final String MICROSOFT = "Microsoft";
+ public static final String DB2 = "DB2";
+
+ // Column names.
+ public static final String ID_COLUMN_NAME = "ID";
+ public static final String CURSOR_KEY_COLUMN_NAME = "CURSOR_KEY";
+ public static final String NAME_COLUMN_NAME = "NAME";
+ public static final String IDENTIFIER_COLUMN_NAME = "IDENTIFIER";
+ public static final String DESCRIPTION_COLUMN_NAME = "DESCRIPTION";
+ public static final String TENANT_ID_COLUMN_NAME = "TENANT_ID";
+ 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 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";
+ public static final String API_RESOURCE_DESCRIPTION_COLUMN_NAME = "API_RESOURCE_DESCRIPTION";
+ public static final String API_RESOURCE_TENANT_ID_COLUMN_NAME = "API_RESOURCE_TENANT_ID";
+ public static final String API_RESOURCE_TYPE_COLUMN_NAME = "API_RESOURCE_TYPE";
+ public static final String SCOPE_ID_COLUMN_NAME = "SCOPE_ID";
+ 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";
+
+ // Database constraint names.
+ public static final String API_RESOURCE_UNIQUE_CONSTRAINT = "identifier_unique";
+ public static final String SCOPE_UNIQUE_CONSTRAINT = "scope_unique";
+ public static final String DB2_SQL_ERROR_CODE_UNIQUE_CONSTRAINT = "-803";
+
+ // SQL queries.
+ public static final String GET_API_RESOURCES = "SELECT ID, CURSOR_KEY, NAME, IDENTIFIER, DESCRIPTION, TENANT_ID," +
+ " TYPE, REQUIRES_AUTHORIZATION FROM API_RESOURCE WHERE ";
+ public static final String GET_API_RESOURCES_MSSQL = "SELECT TOP(%d) ID, CURSOR_KEY, NAME, IDENTIFIER, " +
+ "DESCRIPTION, TENANT_ID, TYPE, REQUIRES_AUTHORIZATION FROM API_RESOURCE WHERE ";
+ public static final String GET_API_RESOURCES_TAIL =
+ " 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_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" +
+ " AR.ID AS API_RESOURCE_ID," +
+ " 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," +
+ " S.ID AS SCOPE_ID," +
+ " S.NAME AS SCOPE_QUALIFIED_NAME," +
+ " S.DISPLAY_NAME AS SCOPE_DISPLAY_NAME," +
+ " S.DESCRIPTION AS SCOPE_DESCRIPTION" +
+ " FROM API_RESOURCE AR LEFT JOIN SCOPE S ON AR.ID = S.API_ID WHERE AR.ID = ? AND AR.TENANT_ID = ?";
+ public static final String GET_SCOPES_BY_API_ID = "SELECT ID, NAME, DISPLAY_NAME, DESCRIPTION, API_ID, TENANT_ID "
+ + "FROM SCOPE WHERE API_ID = ? AND TENANT_ID = ?";
+ public static final String GET_API_RESOURCE_BY_IDENTIFIER = "SELECT" +
+ " AR.ID AS API_RESOURCE_ID," +
+ " 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," +
+ " S.ID AS SCOPE_ID," +
+ " S.NAME AS SCOPE_QUALIFIED_NAME," +
+ " S.DISPLAY_NAME AS SCOPE_DISPLAY_NAME," +
+ " S.DESCRIPTION AS SCOPE_DESCRIPTION" +
+ " FROM API_RESOURCE AR LEFT JOIN SCOPE S ON AR.ID = S.API_ID WHERE AR.IDENTIFIER = ? AND AR.TENANT_ID = ?";
+ public static final String IS_API_RESOURCE_EXIST_BY_IDENTIFIER = "SELECT ID FROM API_RESOURCE WHERE " +
+ "IDENTIFIER = ? AND TENANT_ID = ?";
+ public static final String ADD_API_RESOURCE = "INSERT INTO API_RESOURCE (ID, TYPE, "
+ + "NAME, IDENTIFIER, DESCRIPTION, TENANT_ID, REQUIRES_AUTHORIZATION) VALUES (?, ?, ?, ?, ?, ?, ?)";
+ public static final String ADD_SCOPE = "INSERT INTO SCOPE (ID, "
+ + "NAME, DISPLAY_NAME, DESCRIPTION, API_ID, TENANT_ID) VALUES (?, ?, ?, ?, ?, ?)";
+ public static final String DELETE_API_RESOURCE = "DELETE FROM API_RESOURCE WHERE ID = ? AND TENANT_ID = ?";
+ public static final String DELETE_SCOPES_BY_API = "DELETE FROM SCOPE WHERE API_ID = ? AND TENANT_ID = ?";
+ public static final String UPDATE_API_RESOURCE = "UPDATE API_RESOURCE SET NAME = ?, DESCRIPTION = ?" +
+ " WHERE ID = ?";
+ public static final String IS_SCOPE_EXIST_BY_ID = "SELECT ID FROM SCOPE WHERE ID = ? AND TENANT_ID = ?";
+ public static final String GET_SCOPE_BY_NAME = "SELECT ID, NAME, DISPLAY_NAME, DESCRIPTION, API_ID, TENANT_ID "
+ + "FROM SCOPE WHERE NAME = ? AND TENANT_ID = ?";
+ public static final String GET_SCOPE_BY_NAME_API_ID = "SELECT ID, NAME, DISPLAY_NAME, DESCRIPTION, API_ID, " +
+ "TENANT_ID FROM SCOPE WHERE NAME = ? AND API_ID = ? AND TENANT_ID = ?";
+ public static final String GET_SCOPES_BY_TENANT_ID = "SELECT ID, NAME, DISPLAY_NAME, DESCRIPTION, API_ID, " +
+ "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 = ?";
+}
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
new file mode 100644
index 000000000000..60ca1557ff20
--- /dev/null
+++ 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
@@ -0,0 +1,234 @@
+/*
+ * 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.api.resource.mgt.dao;
+
+import org.wso2.carbon.identity.api.resource.mgt.APIResourceMgtException;
+import org.wso2.carbon.identity.api.resource.mgt.APIResourceMgtServerException;
+import org.wso2.carbon.identity.application.common.model.APIResource;
+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;
+
+import java.util.List;
+
+/**
+ * This interface performs CRUD operations for {@link APIResource} and {@link Scope}
+ */
+public interface APIResourceManagementDAO {
+
+ /**
+ * 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.
+ * @return List of APIResource
.
+ * @throws APIResourceMgtException If an error occurs while retrieving the API resources.
+ */
+ List getAPIResources(Integer limit, Integer tenantId, String sortOrder,
+ List expressionNodes) throws APIResourceMgtException;
+
+ /**
+ * Retrieve the count of API resources under a given tenantId.
+ *
+ * @param tenantId Tenant Id.
+ * @param expressionNodes Expression nodes.
+ * @return Count of API resources.
+ * @throws APIResourceMgtException If an error occurs while retrieving the count.
+ */
+ Integer getAPIResourcesCount(Integer tenantId, List expressionNodes)
+ throws APIResourceMgtException;
+
+ /**
+ * Retrieve the scopes of a given API resource.
+ *
+ * @param apiId API resource id.
+ * @return List of Scope
.
+ * @throws APIResourceMgtServerException If an error occurs while retrieving the scopes.
+ */
+ List getScopesByAPI(String apiId, Integer tenantId) throws APIResourceMgtServerException;
+
+ /**
+ * Create a new {@link APIResource}.
+ *
+ * @param apiResource API resource.
+ * @param tenantId Tenant Id.
+ * @return Created APIResource
.
+ * @throws APIResourceMgtException If an error occurs while creating the API resource.
+ */
+ APIResource addAPIResource(APIResource apiResource, Integer tenantId) throws APIResourceMgtException;
+
+ /**
+ * Check if {@link APIResource} exists for the given identifier.
+ *
+ * @param identifier API resource identifier.
+ * @param tenantId Tenant Id.
+ * @return True if exists, false otherwise.
+ * @throws APIResourceMgtException If an error occurs while checking the existence.
+ */
+ boolean isAPIResourceExist(String identifier, Integer tenantId) throws APIResourceMgtException;
+
+ /**
+ * Check if {@link APIResource} exists for the given id.
+ *
+ * @param apiId API resource id.
+ * @param tenantId Tenant Id.
+ * @return True if exists, false otherwise.
+ * @throws APIResourceMgtException If an error occurs while checking the existence.
+ */
+ boolean isAPIResourceExistById(String apiId, Integer tenantId) throws APIResourceMgtException;
+
+ /**
+ * Retrieve the {@link APIResource} for the given id.
+ *
+ * @param apiId API resource id.
+ * @param tenantId Tenant Id.
+ * @return An APIResource
.
+ * @throws APIResourceMgtException If an error occurs while retrieving the API resource.
+ */
+ APIResource getAPIResourceById(String apiId, Integer tenantId) throws APIResourceMgtException;
+
+ /**
+ * Retrieve the {@link APIResource} for the given identifier.
+ *
+ * @param identifier API resource identifier.
+ * @param tenantId Tenant Id.
+ * @return An APIResource
.
+ * @throws APIResourceMgtException If an error occurs while retrieving the API resource.
+ */
+ APIResource getAPIResourceByIdentifier(String identifier, Integer tenantId) throws APIResourceMgtException;
+
+ /**
+ * Update the {@link APIResource} for the given id.
+ *
+ * @param apiResource API resource.
+ * @param tenantId Tenant Id.
+ * @throws APIResourceMgtException If an error occurs while updating the API resource.
+ */
+ void updateAPIResource(APIResource apiResource, List addedScopes, List removedScopes,
+ Integer tenantId) throws APIResourceMgtException;
+
+ /**
+ * Delete the {@link APIResource} for the given id.
+ *
+ * @param apiId API resource id.
+ * @param tenantId Tenant Id.
+ * @throws APIResourceMgtException If an error occurs while deleting the API resource.
+ */
+ void deleteAPIResourceById(String apiId, Integer tenantId) throws APIResourceMgtException;
+
+ /**
+ * Is {@link Scope} exist by name.
+ *
+ * @param name Scope name.
+ * @param tenantId Tenant Id.
+ * @return True if exists, false otherwise.
+ * @throws APIResourceMgtException If an error occurs while checking the existence.
+ */
+ boolean isScopeExistByName(String name, Integer tenantId) throws APIResourceMgtException;
+
+ /**
+ * Is {@link Scope} exist by Id.
+ *
+ * @param scopeId Scope Id.
+ * @return True if exists, false otherwise.
+ * @throws APIResourceMgtException If an error occurs while checking the existence.
+ */
+ boolean isScopeExistById(String scopeId, Integer tenantId) throws APIResourceMgtException;
+
+ /**
+ * Retrieve the {@link Scope} for the given name and tenantDomain.
+ *
+ * @param name Scope name.
+ * @param tenantId Tenant Id.
+ * @return An Scope
.
+ * @throws APIResourceMgtException If an error occurs while retrieving the scope.
+ */
+ Scope getScopeByNameAndTenantId(String name, Integer tenantId) throws APIResourceMgtException;
+
+ /**
+ * Retrieve the {@link Scope} for the given name, apiId and tenantId.
+ *
+ * @param name Scope name.
+ * @param tenantId Tenant Id.
+ * @param apiId API resource id.
+ * @return An Scope
.
+ * @throws APIResourceMgtException If an error occurs while retrieving the scope.
+ */
+ Scope getScopeByNameTenantIdAPIId(String name, Integer tenantId, String apiId)
+ throws APIResourceMgtException;
+
+ /**
+ * Retrieve the {@link Scope} for the given tenantId.
+ *
+ * @param tenantId Tenant Id.
+ * @param expressionNodes Expression nodes.
+ * @return List of Scope
.
+ * @throws APIResourceMgtException If an error occurs while retrieving the scope.
+ */
+ List getScopesByTenantId(Integer tenantId, List expressionNodes)
+ throws APIResourceMgtException;
+
+ /**
+ * Add a new {@link Scope} to a given API resource.
+ *
+ * @param scopes List of scopes.
+ * @param apiId API resource id.
+ * @throws APIResourceMgtException If an error occurs while adding the scopes.
+ */
+ void addScopes(List scopes, String apiId, Integer tenantId) throws APIResourceMgtException;
+
+ /**
+ * Delete all the {@link Scope} for the given apiId.
+ *
+ * @param apiId API resource id.
+ * @throws APIResourceMgtException If an error occurs while deleting the scopes.
+ */
+ void deleteAllScopes(String apiId, Integer tenantId) throws APIResourceMgtException;
+
+ /**
+ * Delete the {@link Scope} for the given scopeId.
+ *
+ * @param scopeName Scope id.
+ * @throws APIResourceMgtException If an error occurs while deleting the scope.
+ */
+ void deleteScope(String apiId, String scopeName, Integer tenantId) throws APIResourceMgtException;
+
+ /**
+ * Put scopes to the given API resource.
+ *
+ * @param apiId API resource id.
+ * @param currentScopes Current scopes.
+ * @param scopes New scopes.
+ * @param tenantId Tenant Id.
+ * @throws APIResourceMgtException If an error occurs while putting the scopes.
+ */
+ void putScopes(String apiId, List currentScopes, List scopes, Integer tenantId)
+ throws APIResourceMgtException;
+
+ /**
+ * Retrieve the subscribed applications for the given apiId.
+ *
+ * @param apiId API resource id.
+ * @return List of ApplicationBasicInfo
.
+ * @throws APIResourceMgtException If an error occurs while retrieving the subscribed applications.
+ */
+ List getSubscribedApplications(String apiId) 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/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
new file mode 100644
index 000000000000..53badbe6ba5b
--- /dev/null
+++ 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
@@ -0,0 +1,852 @@
+/*
+ * 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.api.resource.mgt.dao.impl;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+import org.wso2.carbon.identity.api.resource.mgt.APIResourceMgtClientException;
+import org.wso2.carbon.identity.api.resource.mgt.APIResourceMgtException;
+import org.wso2.carbon.identity.api.resource.mgt.APIResourceMgtServerException;
+import org.wso2.carbon.identity.api.resource.mgt.constant.APIResourceManagementConstants;
+import org.wso2.carbon.identity.api.resource.mgt.constant.SQLConstants;
+import org.wso2.carbon.identity.api.resource.mgt.dao.APIResourceManagementDAO;
+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.ApplicationBasicInfo;
+import org.wso2.carbon.identity.application.common.model.Scope;
+import org.wso2.carbon.identity.core.model.ExpressionNode;
+import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.wso2.carbon.identity.api.resource.mgt.constant.APIResourceManagementConstants.AFTER;
+import static org.wso2.carbon.identity.api.resource.mgt.constant.APIResourceManagementConstants.BEFORE;
+
+/**
+ * This class implements the {@link APIResourceManagementDAO} interface.
+ */
+public class APIResourceManagementDAOImpl implements APIResourceManagementDAO {
+
+ @Override
+ public List getAPIResources(Integer limit, Integer tenantId, String sortOrder,
+ List expressionNodes)
+ throws APIResourceMgtException {
+
+ return getAPIResourcesList(limit, tenantId, sortOrder, expressionNodes);
+ }
+
+ @Override
+ public Integer getAPIResourcesCount(Integer tenantId, List expressionNodes)
+ throws APIResourceMgtException {
+
+ // Remove the after and before attributes from the expression nodes list,
+ // since we are not considering them when getting the total count.
+ List expressionNodesCopy = new ArrayList<>(expressionNodes);
+ if (CollectionUtils.isNotEmpty(expressionNodesCopy)) {
+ expressionNodesCopy.removeIf(expressionNode -> AFTER.equals(expressionNode.getAttributeValue()) ||
+ BEFORE.equals(expressionNode.getAttributeValue()));
+ }
+ FilterQueryBuilder filterQueryBuilder = new FilterQueryBuilder();
+ appendFilterQuery(expressionNodesCopy, filterQueryBuilder, false);
+
+ Map filterAttributeValue = filterQueryBuilder.getFilterAttributeValue();
+ String getAPIResourcesCountSqlStmtTail = SQLConstants.GET_API_RESOURCES_COUNT_TAIL;
+
+ String sqlStmt = SQLConstants.GET_API_RESOURCES_COUNT + filterQueryBuilder.getFilterQuery() +
+ getAPIResourcesCountSqlStmtTail;
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false)) {
+ PreparedStatement prepStmt = dbConnection.prepareStatement(sqlStmt);
+ int filterAttrSize = 0;
+ if (filterAttributeValue != null) {
+ for (Map.Entry prepareStatement : filterAttributeValue.entrySet()) {
+ prepStmt.setString(prepareStatement.getKey(), prepareStatement.getValue());
+ }
+ filterAttrSize = filterAttributeValue.entrySet().size();
+ }
+ prepStmt.setInt(filterAttrSize + 1, tenantId);
+ ResultSet rs = prepStmt.executeQuery();
+ if (rs.next()) {
+ return rs.getInt(1);
+ }
+ } catch (SQLException e) {
+ throw new APIResourceMgtServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_RETRIEVING_API_RESOURCES
+ .getCode(),
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_RETRIEVING_API_RESOURCES
+ .getMessage(), e);
+ }
+ return 0;
+ }
+
+ @Override
+ public APIResource addAPIResource(APIResource apiResource, Integer tenantId) throws APIResourceMgtException {
+
+ String generatedAPIId = UUID.randomUUID().toString();
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(true)) {
+ try {
+ PreparedStatement prepStmt = dbConnection.prepareStatement(SQLConstants.ADD_API_RESOURCE);
+ prepStmt.setString(1, generatedAPIId);
+ prepStmt.setString(2, apiResource.getType());
+ prepStmt.setString(3, apiResource.getName());
+ prepStmt.setString(4, apiResource.getIdentifier());
+ prepStmt.setString(5, apiResource.getDescription());
+ prepStmt.setInt(6, tenantId);
+ prepStmt.setBoolean(7, apiResource.isRequiresAuthorization());
+ prepStmt.executeUpdate();
+ prepStmt.clearParameters();
+
+ if (CollectionUtils.isNotEmpty(apiResource.getScopes())) {
+ // Add scopes.
+ addScopes(dbConnection, generatedAPIId, apiResource.getScopes(), tenantId);
+ }
+ IdentityDatabaseUtil.commitTransaction(dbConnection);
+
+ return getAPIResourceById(generatedAPIId, tenantId);
+ } catch (SQLException e) {
+ IdentityDatabaseUtil.rollbackTransaction(dbConnection);
+ if (e.getMessage().toLowerCase().contains(SQLConstants.API_RESOURCE_UNIQUE_CONSTRAINT.toLowerCase())) {
+ throw APIResourceManagementUtil.handleClientException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_API_RESOURCE_ALREADY_EXISTS,
+ String.valueOf(tenantId));
+ }
+ // Handle the DB2 database unique constraint violation error.
+ if (dbConnection.getMetaData().getDatabaseProductName().contains("DB2")) {
+ if (e.getMessage().contains(SQLConstants.DB2_SQL_ERROR_CODE_UNIQUE_CONSTRAINT)) {
+ throw APIResourceManagementUtil.handleClientException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_API_RESOURCE_ALREADY_EXISTS,
+ String.valueOf(tenantId));
+ }
+ }
+ throw e;
+ }
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_ADDING_API_RESOURCE, e);
+ }
+ }
+
+ @Override
+ public List getScopesByAPI(String apiId, Integer tenantId) throws APIResourceMgtServerException {
+
+ List scopes = new ArrayList<>();
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false);
+ PreparedStatement preparedStatement = dbConnection.prepareStatement(SQLConstants.GET_SCOPES_BY_API_ID)) {
+
+ preparedStatement.setString(1, apiId);
+ preparedStatement.setInt(2, tenantId);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ while (resultSet.next()) {
+ Scope scope = new Scope(
+ resultSet.getString(SQLConstants.ID_COLUMN_NAME),
+ resultSet.getString(SQLConstants.NAME_COLUMN_NAME),
+ resultSet.getString(SQLConstants.DISPLAY_NAME_COLUMN_NAME),
+ resultSet.getString(SQLConstants.DESCRIPTION_COLUMN_NAME)
+ );
+ scopes.add(scope);
+ }
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_RETRIEVING_SCOPES, e);
+ }
+ return scopes;
+ }
+
+ @Override
+ public boolean isAPIResourceExist(String identifier, Integer tenantId) throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false);
+ PreparedStatement preparedStatement =
+ dbConnection.prepareStatement(SQLConstants.IS_API_RESOURCE_EXIST_BY_IDENTIFIER)) {
+
+ preparedStatement.setString(1, identifier);
+ preparedStatement.setInt(2, tenantId);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ if (resultSet.next()) {
+ return true;
+ }
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_CHECKING_API_RESOURCE_EXISTENCE,
+ e);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isAPIResourceExistById(String apiId, Integer tenantId) throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false);
+ PreparedStatement preparedStatement = dbConnection.prepareStatement(SQLConstants.GET_API_RESOURCE_BY_ID)) {
+ preparedStatement.setString(1, apiId);
+ preparedStatement.setInt(2, tenantId);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ if (resultSet.next()) {
+ return true;
+ }
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_CHECKING_API_RESOURCE_EXISTENCE,
+ e);
+ }
+ return false;
+ }
+
+ @Override
+ public APIResource getAPIResourceById(String apiId, Integer tenantId) throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false);
+ PreparedStatement preparedStatement = dbConnection.prepareStatement(SQLConstants.GET_API_RESOURCE_BY_ID)) {
+ preparedStatement.setString(1, apiId);
+ preparedStatement.setInt(2, tenantId);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ return getApiResource(resultSet);
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_RETRIEVING_API_RESOURCES, e);
+ }
+ }
+
+ @Override
+ public APIResource getAPIResourceByIdentifier(String identifier, Integer tenantId)
+ throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false);
+ PreparedStatement preparedStatement =
+ dbConnection.prepareStatement(SQLConstants.GET_API_RESOURCE_BY_IDENTIFIER)) {
+ preparedStatement.setString(1, identifier);
+ preparedStatement.setInt(2, tenantId);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ return getApiResource(resultSet);
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_RETRIEVING_API_RESOURCES, e);
+ }
+ }
+
+ @Override
+ public void updateAPIResource(APIResource apiResource, List addedScopes, List removedScopes,
+ Integer tenantId) throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(true);
+ PreparedStatement preparedStatement = dbConnection.prepareStatement(SQLConstants.UPDATE_API_RESOURCE)) {
+ try {
+ preparedStatement.setString(1, apiResource.getName());
+ preparedStatement.setString(2, apiResource.getDescription());
+ preparedStatement.setString(3, apiResource.getId());
+ preparedStatement.executeUpdate();
+
+ if (CollectionUtils.isNotEmpty(addedScopes)) {
+ // Add Scopes.
+ addScopes(dbConnection, apiResource.getId(), addedScopes, tenantId);
+ }
+
+ IdentityDatabaseUtil.commitTransaction(dbConnection);
+ } catch (SQLException e) {
+ IdentityDatabaseUtil.rollbackTransaction(dbConnection);
+ throw e;
+ }
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_UPDATING_API_RESOURCE, e);
+ }
+ }
+
+ @Override
+ public void deleteAPIResourceById(String apiId, Integer tenantId) throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(true)) {
+ try {
+ PreparedStatement prepStmt = dbConnection.prepareStatement(SQLConstants.DELETE_SCOPES_BY_API);
+ prepStmt.setString(1, apiId);
+ prepStmt.setInt(2, tenantId);
+ prepStmt.executeUpdate();
+
+ prepStmt = dbConnection.prepareStatement(SQLConstants.DELETE_API_RESOURCE);
+ prepStmt.setString(1, apiId);
+ prepStmt.setInt(2, tenantId);
+ prepStmt.executeUpdate();
+
+ IdentityDatabaseUtil.commitTransaction(dbConnection);
+ } catch (SQLException e) {
+ IdentityDatabaseUtil.rollbackTransaction(dbConnection);
+ throw e;
+ }
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_DELETING_API_RESOURCE, e);
+ }
+ }
+
+ @Override
+ public boolean isScopeExistByName(String name, Integer tenantId) throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false);
+ PreparedStatement preparedStatement = dbConnection.prepareStatement(SQLConstants.GET_SCOPE_BY_NAME)) {
+ preparedStatement.setString(1, name);
+ preparedStatement.setInt(2, tenantId);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ return resultSet.next();
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_CHECKING_EXISTENCE_OF_SCOPE, e);
+ }
+ }
+
+ @Override
+ public boolean isScopeExistById(String scopeId, Integer tenantId) throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false);
+ PreparedStatement preparedStatement = dbConnection.prepareStatement(SQLConstants.IS_SCOPE_EXIST_BY_ID)) {
+ preparedStatement.setString(1, scopeId);
+ preparedStatement.setInt(2, tenantId);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ if (resultSet.next()) {
+ return true;
+ }
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_CHECKING_EXISTENCE_OF_SCOPE, e);
+ }
+ return false;
+ }
+
+ @Override
+ public Scope getScopeByNameAndTenantId(String name, Integer tenantId) throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false);
+ PreparedStatement preparedStatement = dbConnection.prepareStatement(SQLConstants.GET_SCOPE_BY_NAME)) {
+ preparedStatement.setString(1, name);
+ preparedStatement.setInt(2, tenantId);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ if (resultSet.next()) {
+ return new Scope(
+ resultSet.getString(SQLConstants.ID_COLUMN_NAME),
+ resultSet.getString(SQLConstants.NAME_COLUMN_NAME),
+ resultSet.getString(SQLConstants.DISPLAY_NAME_COLUMN_NAME),
+ resultSet.getString(SQLConstants.DESCRIPTION_COLUMN_NAME)
+ );
+ }
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_GETTING_SCOPES, e);
+ }
+ return null;
+ }
+
+ @Override
+ public Scope getScopeByNameTenantIdAPIId(String name, Integer tenantId, String apiId)
+ throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false);
+ PreparedStatement preparedStatement =
+ dbConnection.prepareStatement(SQLConstants.GET_SCOPE_BY_NAME_API_ID)) {
+ preparedStatement.setString(1, name);
+ preparedStatement.setString(2, apiId);
+ preparedStatement.setInt(3, tenantId);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ if (resultSet.next()) {
+ return new Scope(
+ resultSet.getString(SQLConstants.ID_COLUMN_NAME),
+ resultSet.getString(SQLConstants.NAME_COLUMN_NAME),
+ resultSet.getString(SQLConstants.DISPLAY_NAME_COLUMN_NAME),
+ resultSet.getString(SQLConstants.DESCRIPTION_COLUMN_NAME)
+ );
+ }
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_GETTING_SCOPES, e);
+ }
+ return null;
+ }
+
+ @Override
+ public List getScopesByTenantId(Integer tenantId, List expressionNodes)
+ throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false)) {
+ FilterQueryBuilder filterQueryBuilder = new FilterQueryBuilder();
+ appendFilterQuery(expressionNodes, filterQueryBuilder, true);
+ String query = SQLConstants.GET_SCOPES_BY_TENANT_ID + filterQueryBuilder.getFilterQuery() +
+ SQLConstants.GET_SCOPES_BY_TENANT_ID_TAIL;
+ PreparedStatement preparedStatement = dbConnection.prepareStatement(query);
+ preparedStatement.setInt(1, tenantId);
+ int filterAttrSize = 0;
+ if (filterQueryBuilder.getFilterAttributeValue() != null) {
+ for (Map.Entry prepareStatement :
+ filterQueryBuilder.getFilterAttributeValue().entrySet()) {
+ preparedStatement.setString(prepareStatement.getKey(), prepareStatement.getValue());
+ }
+ filterAttrSize = filterQueryBuilder.getFilterAttributeValue().entrySet().size();
+ }
+ preparedStatement.setInt(filterAttrSize + 1, tenantId);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ List scopesList = new ArrayList<>();
+ while (resultSet.next()) {
+ scopesList.add(new Scope(
+ resultSet.getString(SQLConstants.ID_COLUMN_NAME),
+ resultSet.getString(SQLConstants.NAME_COLUMN_NAME),
+ resultSet.getString(SQLConstants.DISPLAY_NAME_COLUMN_NAME),
+ resultSet.getString(SQLConstants.DESCRIPTION_COLUMN_NAME)
+ ));
+ }
+ return scopesList;
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_GETTING_SCOPES, e);
+ }
+ }
+
+ @Override
+ public void addScopes(List scopes, String apiId, Integer tenantId) throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(true)) {
+ addScopes(dbConnection, apiId, scopes, tenantId);
+ IdentityDatabaseUtil.commitTransaction(dbConnection);
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_ADDING_SCOPES, e);
+ }
+ }
+
+ @Override
+ public void deleteAllScopes(String apiId, Integer tenantId) throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(true)) {
+ deleteScopeByAPIId(dbConnection, apiId, tenantId);
+ IdentityDatabaseUtil.commitTransaction(dbConnection);
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_DELETING_SCOPES, e);
+ }
+ }
+
+ @Override
+ public void deleteScope(String apiId, String scopeName, Integer tenantId) throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(true)) {
+ deleteScopeByName(dbConnection, scopeName, tenantId);
+ IdentityDatabaseUtil.commitTransaction(dbConnection);
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_DELETING_SCOPES, e);
+ }
+ }
+
+ @Override
+ public void putScopes(String apiId, List currentScopes, List scopes, Integer tenantId)
+ throws APIResourceMgtException {
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(true)) {
+ try {
+ // Delete the existing scopes and commit.
+ deleteScopeByAPIId(dbConnection, apiId, tenantId);
+ IdentityDatabaseUtil.commitTransaction(dbConnection);
+ // Add the new scopes and commit.
+ addScopes(dbConnection, apiId, scopes, tenantId);
+ IdentityDatabaseUtil.commitTransaction(dbConnection);
+ } catch (APIResourceMgtException e) {
+
+ // Rollback the transaction if any error occurred and add back the previous scopes.
+ IdentityDatabaseUtil.rollbackTransaction(dbConnection);
+ addScopes(dbConnection, apiId, currentScopes, tenantId);
+ IdentityDatabaseUtil.commitTransaction(dbConnection);
+ throw e;
+ }
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_UPDATING_SCOPES, e);
+ }
+ }
+
+ @Override
+ public List getSubscribedApplications(String apiId) {
+ return null;
+ }
+
+ /**
+ * 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 getAPIResourcesList(Integer limit, Integer tenantId, String sortOrder,
+ List expressionNodes)
+ throws APIResourceMgtException {
+
+ FilterQueryBuilder filterQueryBuilder = new FilterQueryBuilder();
+ appendFilterQuery(expressionNodes, filterQueryBuilder, false);
+ Map filterAttributeValue = filterQueryBuilder.getFilterAttributeValue();
+
+ List apiResources = new ArrayList<>();
+
+ try (Connection dbConnection = IdentityDatabaseUtil.getDBConnection(false)) {
+
+ String databaseName = dbConnection.getMetaData().getDatabaseProductName();
+ String sqlStmt = buildAPIResourcesSqlStatement(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()) {
+ APIResource.APIResourceBuilder apiResourceBuilder = new APIResource.APIResourceBuilder()
+ .id(rs.getString(SQLConstants.ID_COLUMN_NAME))
+ .cursorKey(rs.getInt(SQLConstants.CURSOR_KEY_COLUMN_NAME))
+ .name(rs.getString(SQLConstants.NAME_COLUMN_NAME))
+ .identifier(rs.getString(SQLConstants.IDENTIFIER_COLUMN_NAME))
+ .description(rs.getString(SQLConstants.DESCRIPTION_COLUMN_NAME))
+ .type(rs.getString(SQLConstants.TYPE_COLUMN_NAME))
+ .requiresAuthorization(rs.getBoolean(SQLConstants.REQUIRES_AUTHORIZATION_COLUMN_NAME))
+ .tenantId(rs.getInt(SQLConstants.TENANT_ID_COLUMN_NAME));
+ apiResources.add(apiResourceBuilder.build());
+ }
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_RETRIEVING_API_RESOURCES, e);
+ }
+ return apiResources;
+ }
+
+ /**
+ * Get API resource from the result set.
+ *
+ * @param resultSet Result set.
+ * @return API resource.
+ * @throws SQLException If an error occurs while retrieving API resource.
+ */
+ private static APIResource getApiResource(ResultSet resultSet) throws SQLException {
+
+ List scopes = new ArrayList<>();
+ APIResource apiResource = null;
+ while (resultSet.next()) {
+ if (apiResource == null) {
+ APIResource.APIResourceBuilder apiResourceBuilder = new APIResource.APIResourceBuilder()
+ .id(resultSet.getString(SQLConstants.API_RESOURCE_ID_COLUMN_NAME))
+ .name(resultSet.getString(SQLConstants.API_RESOURCE_NAME_COLUMN_NAME))
+ .identifier(resultSet.getString(SQLConstants.API_RESOURCE_IDENTIFIER_COLUMN_NAME))
+ .description(resultSet.getString(SQLConstants.API_RESOURCE_DESCRIPTION_COLUMN_NAME))
+ .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));
+ 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 (apiResource != null) {
+ apiResource.setScopes(scopes);
+ }
+ return apiResource;
+ }
+
+ /**
+ * Build the SQL statement to retrieve API resources.
+ *
+ * @param databaseName Database name.
+ * @param tenantId Tenant ID.
+ * @param filterQuery Filter query.
+ * @param sortOrder Sort order.
+ * @param limit Limit.
+ * @return SQL statement to retrieve API resources.
+ */
+ private String buildAPIResourcesSqlStatement(String databaseName, Integer tenantId, String filterQuery,
+ String sortOrder, Integer limit) {
+
+ String sqlStmtHead = SQLConstants.GET_API_RESOURCES;
+ String sqlStmtTail = SQLConstants.GET_API_RESOURCES_TAIL;
+
+ if (databaseName.contains(SQLConstants.MICROSOFT)) {
+ sqlStmtHead = SQLConstants.GET_API_RESOURCES_MSSQL;
+ sqlStmtTail = SQLConstants.GET_API_RESOURCES_TAIL_MSSQL;
+
+ return String.format(sqlStmtHead, limit) + filterQuery + String.format(sqlStmtTail, tenantId, sortOrder);
+ }
+
+ return sqlStmtHead + filterQuery + String.format(sqlStmtTail, tenantId, sortOrder, limit);
+ }
+
+ /**
+ * Delete scopes by name.
+ *
+ * @param dbConnection Database connection.
+ * @param scopeName Scope name.
+ * @param tenantId Tenant ID.
+ * @throws APIResourceMgtException If an error occurs while deleting scopes.
+ */
+ private void deleteScopeByName(Connection dbConnection, String scopeName, Integer tenantId)
+ throws APIResourceMgtException {
+
+ try {
+ PreparedStatement prepStmt = dbConnection.prepareStatement(SQLConstants.DELETE_SCOPE_BY_NAME);
+ prepStmt.setString(1, scopeName);
+ prepStmt.setInt(2, tenantId);
+ prepStmt.executeUpdate();
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_DELETING_SCOPES, e);
+ }
+ }
+
+ /**
+ * Delete scopes by API ID.
+ *
+ * @param dbConnection Database connection.
+ * @param apiId API ID.
+ * @param tenantId Tenant ID.
+ * @throws APIResourceMgtException If an error occurs while deleting scopes.
+ */
+ private void deleteScopeByAPIId(Connection dbConnection, String apiId, Integer tenantId)
+ throws APIResourceMgtException {
+
+ try (PreparedStatement prepStmt = dbConnection.prepareStatement(SQLConstants.DELETE_SCOPES_BY_API)) {
+ prepStmt.setString(1, apiId);
+ prepStmt.setInt(2, tenantId);
+ prepStmt.executeUpdate();
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_DELETING_SCOPES, e);
+ }
+ }
+
+ /**
+ * Add scopes to the API resource.
+ *
+ * @param dbConnection Database connection.
+ * @param apiId API resource id.
+ * @param scopes List of scopes.
+ * @param tenantId Tenant id.
+ * @throws APIResourceMgtException If an error occurs while adding scopes.
+ */
+ private void addScopes(Connection dbConnection, String apiId, List scopes, Integer tenantId)
+ throws APIResourceMgtException {
+
+ if (CollectionUtils.isEmpty(scopes)) {
+ return;
+ }
+
+ try {
+ try {
+ PreparedStatement prepStmt = dbConnection.prepareStatement(SQLConstants.ADD_SCOPE);
+ for (Scope scope : scopes) {
+ prepStmt.setString(1, UUID.randomUUID().toString());
+ prepStmt.setString(2, scope.getName());
+ prepStmt.setString(3, scope.getDisplayName());
+ prepStmt.setString(4, scope.getDescription());
+ prepStmt.setString(5, apiId);
+ prepStmt.setInt(6, tenantId);
+ prepStmt.addBatch();
+ }
+ prepStmt.executeBatch();
+ } catch (SQLException e) {
+ if (e.getMessage().toLowerCase().contains(SQLConstants.SCOPE_UNIQUE_CONSTRAINT.toLowerCase())) {
+ throw APIResourceManagementUtil.handleClientException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_SCOPE_ALREADY_EXISTS,
+ String.valueOf(tenantId));
+ }
+ // Handle DB2 database unique constraint violation error.
+ if (dbConnection.getMetaData().getDatabaseProductName().contains(SQLConstants.DB2)) {
+ if (e.getNextException().getMessage().toLowerCase().contains(
+ SQLConstants.DB2_SQL_ERROR_CODE_UNIQUE_CONSTRAINT)) {
+ throw APIResourceManagementUtil.handleClientException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_SCOPE_ALREADY_EXISTS,
+ String.valueOf(tenantId));
+ }
+ }
+ throw e;
+ }
+ } catch (SQLException e) {
+ throw APIResourceManagementUtil.handleServerException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_ERROR_WHILE_ADDING_SCOPES, e);
+ }
+ }
+
+ /**
+ * Append the filter query to the query builder.
+ *
+ * @param expressionNodes List of expression nodes.
+ * @param filterQueryBuilder Filter query builder.
+ * @param isScopeFilter Whether the filter is for scopes.
+ * @throws APIResourceMgtClientException If an error occurs while appending the filter query.
+ */
+ private void appendFilterQuery(List expressionNodes, FilterQueryBuilder filterQueryBuilder,
+ boolean isScopeFilter) throws APIResourceMgtClientException {
+
+ int count = 1;
+ StringBuilder filter = new StringBuilder();
+ if (CollectionUtils.isEmpty(expressionNodes)) {
+ filterQueryBuilder.setFilterQuery(StringUtils.EMPTY);
+ } else {
+ for (ExpressionNode expressionNode : expressionNodes) {
+ String operation = expressionNode.getOperation();
+ String value = expressionNode.getValue();
+ String attributeValue = expressionNode.getAttributeValue();
+ String attributeName = APIResourceManagementConstants.ATTRIBUTE_COLUMN_MAP.get(attributeValue);
+
+ // If the filter is for scopes, get the column name from the scope attribute map.
+ if (isScopeFilter) {
+ attributeName = APIResourceManagementConstants.SCOPE_ATTRIBUTE_COLUMN_MAP.get(attributeValue);
+ }
+
+ if (StringUtils.isNotBlank(attributeName) && StringUtils.isNotBlank(value) && StringUtils
+ .isNotBlank(operation)) {
+ switch (operation) {
+ case APIResourceManagementConstants.EQ: {
+ equalFilterBuilder(count, value, attributeName, filter, filterQueryBuilder);
+ ++count;
+ break;
+ }
+ case APIResourceManagementConstants.SW: {
+ startWithFilterBuilder(count, value, attributeName, filter, filterQueryBuilder);
+ ++count;
+ break;
+ }
+ case APIResourceManagementConstants.EW: {
+ endWithFilterBuilder(count, value, attributeName, filter, filterQueryBuilder);
+ ++count;
+ break;
+ }
+ case APIResourceManagementConstants.CO: {
+ containsFilterBuilder(count, value, attributeName, filter, filterQueryBuilder);
+ ++count;
+ break;
+ }
+ case APIResourceManagementConstants.GE: {
+ greaterThanOrEqualFilterBuilder(count, value, attributeName, filter, filterQueryBuilder);
+ ++count;
+ break;
+ }
+ case APIResourceManagementConstants.LE: {
+ lessThanOrEqualFilterBuilder(count, value, attributeName, filter, filterQueryBuilder);
+ ++count;
+ break;
+ }
+ case APIResourceManagementConstants.GT: {
+ greaterThanFilterBuilder(count, value, attributeName, filter, filterQueryBuilder);
+ ++count;
+ break;
+ }
+ case APIResourceManagementConstants.LT: {
+ lessThanFilterBuilder(count, value, attributeName, filter, filterQueryBuilder);
+ ++count;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ } else {
+ throw APIResourceManagementUtil.handleClientException(
+ APIResourceManagementConstants.ErrorMessages.ERROR_CODE_INVALID_FILTER_VALUE);
+ }
+ }
+ if (StringUtils.isBlank(filter.toString())) {
+ filterQueryBuilder.setFilterQuery(StringUtils.EMPTY);
+ } else {
+ filterQueryBuilder.setFilterQuery(filter.toString());
+ }
+ }
+ }
+
+ private void equalFilterBuilder(int count, String value, String attributeName, StringBuilder filter,
+ FilterQueryBuilder filterQueryBuilder) {
+
+ String filterString = " = ? AND ";
+ filter.append(attributeName).append(filterString);
+ filterQueryBuilder.setFilterAttributeValue(count, value);
+ }
+
+ private void startWithFilterBuilder(int count, String value, String attributeName, StringBuilder filter,
+ FilterQueryBuilder filterQueryBuilder) {
+
+ String filterString = " LIKE ? AND ";
+ filter.append(attributeName).append(filterString);
+ filterQueryBuilder.setFilterAttributeValue(count, value + "%");
+ }
+
+ private void endWithFilterBuilder(int count, String value, String attributeName, StringBuilder filter,
+ FilterQueryBuilder filterQueryBuilder) {
+
+ String filterString = " LIKE ? AND ";
+ filter.append(attributeName).append(filterString);
+ filterQueryBuilder.setFilterAttributeValue(count, "%" + value);
+ }
+
+ private void containsFilterBuilder(int count, String value, String attributeName, StringBuilder filter,
+ FilterQueryBuilder filterQueryBuilder) {
+
+ String filterString = " LIKE ? AND ";
+ filter.append(attributeName).append(filterString);
+ filterQueryBuilder.setFilterAttributeValue(count, "%" + value + "%");
+ }
+
+ private void greaterThanOrEqualFilterBuilder(int count, String value, String attributeName, StringBuilder filter,
+ FilterQueryBuilder filterQueryBuilder) {
+
+ String filterString = " >= ? AND ";
+ filter.append(attributeName).append(filterString);
+ filterQueryBuilder.setFilterAttributeValue(count, value);
+ }
+
+ private void lessThanOrEqualFilterBuilder(int count, String value, String attributeName, StringBuilder filter,
+ FilterQueryBuilder filterQueryBuilder) {
+
+ String filterString = " <= ? AND ";
+ filter.append(attributeName).append(filterString);
+ filterQueryBuilder.setFilterAttributeValue(count, value);
+ }
+
+ private void greaterThanFilterBuilder(int count, String value, String attributeName, StringBuilder filter,
+ FilterQueryBuilder filterQueryBuilder) {
+
+ String filterString = " > ? AND ";
+ filter.append(attributeName).append(filterString);
+ filterQueryBuilder.setFilterAttributeValue(count, value);
+ }
+
+ private void lessThanFilterBuilder(int count, String value, String attributeName, StringBuilder filter,
+ FilterQueryBuilder filterQueryBuilder) {
+
+ String filterString = " < ? AND ";
+ filter.append(attributeName).append(filterString);
+ filterQueryBuilder.setFilterAttributeValue(count, value);
+ }
+}
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
new file mode 100644
index 000000000000..8104619a3bed
--- /dev/null
+++ 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
@@ -0,0 +1,321 @@
+/*
+ * 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.api.resource.mgt.dao.impl;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.identity.api.resource.mgt.APIResourceMgtException;
+import org.wso2.carbon.identity.api.resource.mgt.APIResourceMgtServerException;
+import org.wso2.carbon.identity.api.resource.mgt.cache.APIResourceCacheById;
+import org.wso2.carbon.identity.api.resource.mgt.cache.APIResourceCacheByIdentifier;
+import org.wso2.carbon.identity.api.resource.mgt.cache.APIResourceCacheEntry;
+import org.wso2.carbon.identity.api.resource.mgt.cache.APIResourceIdCacheKey;
+import org.wso2.carbon.identity.api.resource.mgt.cache.APIResourceIdentifierCacheKey;
+import org.wso2.carbon.identity.api.resource.mgt.dao.APIResourceManagementDAO;
+import org.wso2.carbon.identity.application.common.model.APIResource;
+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;
+
+import java.util.List;
+
+/**
+ * This class implements the {@link APIResourceManagementDAO} interface.
+ */
+public class CacheBackedAPIResourceMgtDAO implements APIResourceManagementDAO {
+
+ private static final Log LOG = LogFactory.getLog(CacheBackedAPIResourceMgtDAO.class);
+ private final APIResourceCacheByIdentifier apiResourceCacheByIdentifier;
+ private final APIResourceCacheById apiResourceCacheById;
+ private final APIResourceManagementDAO apiResourceManagementDAO;
+
+ public CacheBackedAPIResourceMgtDAO(APIResourceManagementDAO apiResourceManagementDAO) {
+
+ this.apiResourceManagementDAO = apiResourceManagementDAO;
+ apiResourceCacheByIdentifier = APIResourceCacheByIdentifier.getInstance();
+ apiResourceCacheById = APIResourceCacheById.getInstance();
+ }
+
+ @Override
+ public List getAPIResources(Integer limit, Integer tenantId, String sortOrder,
+ List expressionNodes)
+ throws APIResourceMgtException {
+
+ return apiResourceManagementDAO.getAPIResources(limit, tenantId, sortOrder, expressionNodes);
+ }
+
+ @Override
+ public Integer getAPIResourcesCount(Integer tenantId, List expressionNodes)
+ throws APIResourceMgtException {
+
+ return apiResourceManagementDAO.getAPIResourcesCount(tenantId, expressionNodes);
+ }
+
+ @Override
+ public List getScopesByAPI(String apiId, Integer tenantId) throws APIResourceMgtServerException {
+
+ APIResourceIdCacheKey cacheKey = new APIResourceIdCacheKey(apiId);
+ APIResourceCacheEntry entry = apiResourceCacheById.getValueFromCache(cacheKey, tenantId);
+ if (entry != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Cache entry found for API Resource " + apiId);
+ }
+ return entry.getAPIResource().getScopes();
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Cache entry not found for API Resource " + apiId + ". Fetching entry from DB");
+ }
+ return apiResourceManagementDAO.getScopesByAPI(apiId, tenantId);
+ }
+
+ @Override
+ public APIResource addAPIResource(APIResource apiResource, Integer tenantId) throws APIResourceMgtException {
+
+ return apiResourceManagementDAO.addAPIResource(apiResource, tenantId);
+ }
+
+ @Override
+ public boolean isAPIResourceExist(String identifier, Integer tenantId) throws APIResourceMgtException {
+
+ APIResourceIdentifierCacheKey cacheKey = new APIResourceIdentifierCacheKey(identifier);
+ APIResourceCacheEntry entry = apiResourceCacheByIdentifier.getValueFromCache(cacheKey, tenantId);
+ if (entry != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Cache entry found for API Resource " + identifier);
+ }
+ return true;
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Cache entry not found for API Resource " + identifier + ". Fetching entry from DB");
+ }
+ }
+ return apiResourceManagementDAO.isAPIResourceExist(identifier, tenantId);
+ }
+
+ @Override
+ public boolean isAPIResourceExistById(String apiId, Integer tenantId) throws APIResourceMgtException {
+
+ APIResourceIdCacheKey cacheKey = new APIResourceIdCacheKey(apiId);
+ APIResourceCacheEntry entry = apiResourceCacheById.getValueFromCache(cacheKey, tenantId);
+ if (entry != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Cache entry found for API Resource " + apiId);
+ }
+ return true;
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Cache entry not found for API Resource " + apiId + ". Fetching entry from DB");
+ }
+ return apiResourceManagementDAO.isAPIResourceExistById(apiId, tenantId);
+ }
+
+ @Override
+ public APIResource getAPIResourceById(String apiId, Integer tenantId) throws APIResourceMgtException {
+
+ APIResourceIdCacheKey cacheKey = new APIResourceIdCacheKey(apiId);
+ APIResourceCacheEntry entry = apiResourceCacheById.getValueFromCache(cacheKey, tenantId);
+
+ if (entry != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Cache entry found for API Resource " + apiId);
+ }
+ return entry.getAPIResource();
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Cache entry not found for API Resource " + apiId + ". Fetching entry from DB");
+ }
+
+ APIResource apiResource = apiResourceManagementDAO.getAPIResourceById(apiId, tenantId);
+
+ if (apiResource != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Entry fetched from DB for API Resource " + apiId + ". Updating cache");
+ }
+ apiResourceCacheById.addToCache(cacheKey, new APIResourceCacheEntry(apiResource), tenantId);
+ if (apiResource.getIdentifier() != null) {
+ APIResourceIdentifierCacheKey apiResourceIdentifierCacheKey = new APIResourceIdentifierCacheKey(
+ apiResource.getIdentifier());
+ apiResourceCacheByIdentifier.addToCache(apiResourceIdentifierCacheKey,
+ new APIResourceCacheEntry(apiResource), tenantId);
+ }
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Entry for API Resource " + apiId + " not found in cache or DB");
+ }
+ }
+
+ return apiResource;
+ }
+
+ @Override
+ public APIResource getAPIResourceByIdentifier(String identifier, Integer tenantId)
+ throws APIResourceMgtException {
+
+ APIResourceIdentifierCacheKey cacheKey = new APIResourceIdentifierCacheKey(identifier);
+ APIResourceCacheEntry entry = apiResourceCacheByIdentifier.getValueFromCache(cacheKey, tenantId);
+
+ if (entry != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Cache entry found for API Resource " + identifier);
+ }
+ return entry.getAPIResource();
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Cache entry not found for API Resource " + identifier + ". Fetching entry from DB");
+ }
+ }
+
+ APIResource apiResource = apiResourceManagementDAO.getAPIResourceByIdentifier(identifier, tenantId);
+
+ if (apiResource != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Entry fetched from DB for API Resource " + identifier + ". Updating cache");
+ }
+ apiResourceCacheByIdentifier.addToCache(cacheKey, new APIResourceCacheEntry(apiResource), tenantId);
+ if (apiResource.getId() != null) {
+ APIResourceIdCacheKey apiResourceIdCacheKey = new APIResourceIdCacheKey(apiResource.getId());
+ apiResourceCacheById.addToCache(apiResourceIdCacheKey,
+ new APIResourceCacheEntry(apiResource), tenantId);
+ }
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Entry for API Resource " + identifier + " not found in cache or DB");
+ }
+ }
+
+ return apiResource;
+ }
+
+ @Override
+ public void updateAPIResource(APIResource apiResource, List addedScopes, List removedScopes,
+ Integer tenantId) throws APIResourceMgtException {
+
+ clearAPIResourceCache(apiResource.getIdentifier(), apiResource.getId(), tenantId);
+ apiResourceManagementDAO.updateAPIResource(apiResource, addedScopes, removedScopes, tenantId);
+ }
+
+ @Override
+ public void deleteAPIResourceById(String apiId, Integer tenantId) throws APIResourceMgtException {
+
+ clearAPIResourceCache(null, apiId, tenantId);
+ apiResourceManagementDAO.deleteAPIResourceById(apiId, tenantId);
+ }
+
+ @Override
+ public boolean isScopeExistByName(String name, Integer tenantId) throws APIResourceMgtException {
+
+ return apiResourceManagementDAO.isScopeExistByName(name, tenantId);
+ }
+
+ @Override
+ public boolean isScopeExistById(String scopeId, Integer tenantId) throws APIResourceMgtException {
+
+ return apiResourceManagementDAO.isScopeExistById(scopeId, tenantId);
+ }
+
+ @Override
+ public Scope getScopeByNameAndTenantId(String name, Integer tenantId) throws APIResourceMgtException {
+
+ return apiResourceManagementDAO.getScopeByNameAndTenantId(name, tenantId);
+ }
+
+ @Override
+ public Scope getScopeByNameTenantIdAPIId(String name, Integer tenantId, String apiId)
+ throws APIResourceMgtException {
+
+ return apiResourceManagementDAO.getScopeByNameTenantIdAPIId(name, tenantId, apiId);
+ }
+
+ @Override
+ public List getScopesByTenantId(Integer tenantId, List expressionNodes)
+ throws APIResourceMgtException {
+
+ return apiResourceManagementDAO.getScopesByTenantId(tenantId, expressionNodes);
+ }
+
+ @Override
+ public void addScopes(List scopes, String apiId, Integer tenantId) throws APIResourceMgtException {
+
+ clearAPIResourceCache(null, apiId, tenantId);
+ apiResourceManagementDAO.addScopes(scopes, apiId, tenantId);
+ }
+
+ @Override
+ public void deleteAllScopes(String apiId, Integer tenantId) throws APIResourceMgtException {
+
+ clearAPIResourceCache(null, apiId, tenantId);
+ apiResourceManagementDAO.deleteAllScopes(apiId, tenantId);
+ }
+
+ @Override
+ public void deleteScope(String apiId, String scopeName, Integer tenantId) throws APIResourceMgtException {
+
+ clearAPIResourceCache(null, apiId, tenantId);
+ apiResourceManagementDAO.deleteScope(apiId, scopeName, tenantId);
+ }
+
+ @Override
+ public void putScopes(String apiId, List currentScopes, List scopes, Integer tenantId)
+ throws APIResourceMgtException {
+
+ clearAPIResourceCache(null, apiId, tenantId);
+ apiResourceManagementDAO.putScopes(apiId, currentScopes, scopes, tenantId);
+ }
+
+ @Override
+ public List getSubscribedApplications(String apiId) throws APIResourceMgtException {
+
+ return apiResourceManagementDAO.getSubscribedApplications(apiId);
+ }
+
+ private void clearAPIResourceCache(String identifier, String resourceId, int tenantId) throws
+ APIResourceMgtException {
+
+ // clearing cache entries related to the API Resource.
+ APIResource apiResource = null;
+ if (StringUtils.isNotBlank(resourceId)) {
+ apiResource = this.getAPIResourceById(resourceId, tenantId);
+ }
+ if (StringUtils.isNotBlank(identifier)) {
+ apiResource = this.getAPIResourceByIdentifier(identifier, tenantId);
+ }
+
+ if (apiResource != null) {
+
+ resourceId = resourceId != null ? resourceId : apiResource.getId();
+ identifier = identifier != null ? identifier : apiResource.getIdentifier();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Removing entry for API Resource " + apiResource.getName() + " of tenantId:"
+ + tenantId + " from cache.");
+ }
+
+ APIResourceIdCacheKey apiResourceIdCacheKey = new APIResourceIdCacheKey(resourceId);
+ apiResourceCacheById.clearCacheEntry(apiResourceIdCacheKey, tenantId);
+
+ APIResourceIdentifierCacheKey apiResourceIdentifierCacheKey = new APIResourceIdentifierCacheKey(identifier);
+ apiResourceCacheByIdentifier.clearCacheEntry(apiResourceIdentifierCacheKey, tenantId);
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Entry for API Resource " + identifier + " not found in cache or DB");
+ }
+ }
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/internal/APIResourceManagementServiceComponent.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/internal/APIResourceManagementServiceComponent.java
new file mode 100644
index 000000000000..39128850b270
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/internal/APIResourceManagementServiceComponent.java
@@ -0,0 +1,65 @@
+/*
+ * 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.api.resource.mgt.internal;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.wso2.carbon.identity.api.resource.mgt.APIResourceManager;
+import org.wso2.carbon.identity.api.resource.mgt.APIResourceManagerImpl;
+
+/**
+ * Service component for the API resource management.
+ */
+@Component(
+ name = "api.resource.mgt.service.component",
+ immediate = true
+)
+public class APIResourceManagementServiceComponent {
+
+ private static final Log LOG = LogFactory.getLog(APIResourceManagementServiceComponent.class);
+
+ @Activate
+ protected void activate(ComponentContext context) {
+
+ try {
+ BundleContext bundleCtx = context.getBundleContext();
+ bundleCtx.registerService(APIResourceManager.class, APIResourceManagerImpl.getInstance(), null);
+ LOG.debug("API resource management bundle is activated");
+ } catch (Throwable e) {
+ LOG.error("Error while initializing API resource management component.", e);
+ }
+ }
+
+ @Deactivate
+ protected void deactivate(ComponentContext context) {
+
+ try {
+ BundleContext bundleCtx = context.getBundleContext();
+ bundleCtx.ungetService(bundleCtx.getServiceReference(APIResourceManager.class));
+ LOG.debug("API resource management bundle is deactivated");
+ } catch (Throwable e) {
+ LOG.error("Error while deactivating API resource management component.", e);
+ }
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/model/APIResourceSearchResult.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/model/APIResourceSearchResult.java
new file mode 100644
index 000000000000..f8f6997da072
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/model/APIResourceSearchResult.java
@@ -0,0 +1,52 @@
+/*
+ * 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.api.resource.mgt.model;
+
+import org.wso2.carbon.identity.application.common.model.APIResource;
+
+import java.util.List;
+
+/**
+ * API resource search result.
+ */
+public class APIResourceSearchResult {
+
+ private int totalCount;
+ List apiResources;
+
+ public int getTotalCount() {
+
+ return totalCount;
+ }
+
+ public void setTotalCount(int totalCount) {
+
+ this.totalCount = totalCount;
+ }
+
+ public List getAPIResources() {
+
+ return apiResources;
+ }
+
+ public void setAPIResources(List apiResources) {
+
+ this.apiResources = apiResources;
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/model/FilterQueryBuilder.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/model/FilterQueryBuilder.java
new file mode 100644
index 000000000000..4e216cadeb4b
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/model/FilterQueryBuilder.java
@@ -0,0 +1,74 @@
+/*
+ * 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.api.resource.mgt.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Filter query builder class.
+ */
+public class FilterQueryBuilder {
+
+ private Map stringParameters;
+ private String filter;
+
+ /**
+ * Get filter query builder attributes.
+ *
+ * @return Map of filter query builder attributes.
+ */
+ public Map getFilterAttributeValue() {
+
+ return stringParameters;
+ }
+
+ /**
+ * Add a filter query builder attribute.
+ *
+ * @param value attribute value.
+ */
+ public void setFilterAttributeValue(int count, String value) {
+
+ if (stringParameters == null) {
+ stringParameters = new HashMap<>();
+ }
+ stringParameters.put(count, value);
+ }
+
+ /**
+ * Set filter query.
+ *
+ * @param filter filter query.
+ */
+ public void setFilterQuery(String filter) {
+
+ this.filter = filter;
+ }
+
+ /**
+ * Get filter query.
+ *
+ * @return Filter query string.
+ */
+ public String getFilterQuery() {
+
+ return filter;
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/util/APIResourceManagementUtil.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/util/APIResourceManagementUtil.java
new file mode 100644
index 000000000000..fb152d1be14a
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/java/org/wso2/carbon/identity/api/resource/mgt/util/APIResourceManagementUtil.java
@@ -0,0 +1,67 @@
+/*
+ * 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.api.resource.mgt.util;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.wso2.carbon.identity.api.resource.mgt.APIResourceMgtClientException;
+import org.wso2.carbon.identity.api.resource.mgt.APIResourceMgtServerException;
+import org.wso2.carbon.identity.api.resource.mgt.constant.APIResourceManagementConstants;
+
+/**
+ * Utility class for API Resource Management.
+ */
+public class APIResourceManagementUtil {
+
+ /**
+ * Handle API Resource Management client exceptions.
+ *
+ * @param error Error message.
+ * @param data Data.
+ * @return APIResourceMgtClientException.
+ */
+ public static APIResourceMgtClientException handleClientException(
+ APIResourceManagementConstants.ErrorMessages error, String... data) {
+
+ String description = error.getDescription();
+ if (ArrayUtils.isNotEmpty(data)) {
+ description = String.format(description, data);
+ }
+
+ return new APIResourceMgtClientException(error.getMessage(), description, error.getCode());
+ }
+
+ /**
+ * Handle API Resource Management server exceptions.
+ *
+ * @param error Error message.
+ * @param e Throwable.
+ * @param data Data.
+ * @return APIResourceMgtServerException.
+ */
+ public static APIResourceMgtServerException handleServerException(
+ APIResourceManagementConstants.ErrorMessages error, Throwable e, String... data) {
+
+ String description = error.getDescription();
+ if (ArrayUtils.isNotEmpty(data)) {
+ description = String.format(description, data);
+ }
+
+ return new APIResourceMgtServerException(error.getMessage(), description, error.getCode(), e);
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/resources/META-INF/component.xml b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/resources/META-INF/component.xml
new file mode 100644
index 000000000000..7830fa577f8f
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/main/resources/META-INF/component.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+ Identity
+ /permission/admin/manage/identity
+
+
+ API Resource Management
+ /permission/admin/manage/identity/apiresourcemgt
+
+
+ View
+ /permission/admin/manage/identity/apiresourcemgt/view
+
+
+ Create
+ /permission/admin/manage/identity/apiresourcemgt/create
+
+
+ Update
+ /permission/admin/manage/identity/apiresourcemgt/update
+
+
+ Delete
+ /permission/admin/manage/identity/apiresourcemgt/delete
+
+
+
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceManagerTest.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceManagerTest.java
new file mode 100644
index 000000000000..0809e53b0118
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/java/org/wso2/carbon/identity/api/resource/mgt/APIResourceManagerTest.java
@@ -0,0 +1,366 @@
+/*
+ * 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.api.resource.mgt;
+
+import org.powermock.modules.testng.PowerMockTestCase;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import org.wso2.carbon.context.CarbonContext;
+import org.wso2.carbon.identity.api.resource.mgt.model.APIResourceSearchResult;
+import org.wso2.carbon.identity.application.common.model.APIResource;
+import org.wso2.carbon.identity.application.common.model.Scope;
+import org.wso2.carbon.identity.common.testng.WithAxisConfiguration;
+import org.wso2.carbon.identity.common.testng.WithCarbonHome;
+import org.wso2.carbon.identity.common.testng.WithH2Database;
+import org.wso2.carbon.identity.common.testng.WithRealmService;
+import org.wso2.carbon.identity.common.testng.WithRegistry;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@WithAxisConfiguration
+@WithCarbonHome
+@WithRegistry
+@WithRealmService
+@WithH2Database(files = {"dbscripts/h2.sql"})
+public class APIResourceManagerTest extends PowerMockTestCase {
+
+ private String tenantDomain;
+ private APIResourceManager apiResourceManager;
+
+ @BeforeMethod
+ public void setUp() {
+
+ apiResourceManager = APIResourceManagerImpl.getInstance();
+ tenantDomain = CarbonContext.getThreadLocalCarbonContext().getTenantDomain();
+ }
+
+ @AfterMethod
+ public void tearDown() throws Exception {
+
+ removeTestAPIResources();
+ }
+
+ @DataProvider(name = "getAPIResourceDataProvider")
+ public Object[][] getAPIResourceDataProvider() {
+
+ return new Object[][]{
+ {null, null, 2, null, "ASC", 2},
+ {null, null, 3, null, "DESC", 3},
+ };
+ }
+
+ @Test(dataProvider = "getAPIResourceDataProvider")
+ public void testGetAPIResource(String after, String before, Integer limit, String filter, String sortOrder,
+ int expected) throws Exception {
+
+ addTestAPIResources();
+ APIResourceSearchResult apiResourceSearchResult = apiResourceManager.getAPIResources(after, before, limit,
+ filter, sortOrder, tenantDomain);
+ Assert.assertNotNull(apiResourceSearchResult.getAPIResources());
+ Assert.assertEquals(apiResourceSearchResult.getAPIResources().size(), expected);
+ }
+
+ @Test
+ public void testGetAPIResourceById() throws Exception {
+
+ APIResource createdAPIResource = apiResourceManager.addAPIResource(createAPIResource("test1"),
+ tenantDomain);
+ APIResource apiResource = apiResourceManager.getAPIResourceById(createdAPIResource.getId(), tenantDomain);
+ Assert.assertNotNull(apiResource);
+ Assert.assertEquals(apiResource.getId(), createdAPIResource.getId());
+ }
+
+ @DataProvider(name = "addAPIResourceDataProvider")
+ public Object[][] addAPIResourceDataProvider() {
+
+ APIResource apiResource1 = createAPIResource("1");
+ APIResource.APIResourceBuilder apiResourceBuilder = new APIResource.APIResourceBuilder()
+ .name("testAPIResource name 2")
+ .identifier("testAPIResource identifier 2")
+ .type("BUSINESS");
+ APIResource apiResource2 = apiResourceBuilder.build();
+
+ return new Object[][]{
+ // API resource with scopes.
+ {apiResource1},
+ // API resource with only the identifier.
+ {apiResource2}
+ };
+ }
+
+ @Test(dataProvider = "addAPIResourceDataProvider")
+ public void testAddAPIResource(APIResource apiResource) throws Exception {
+
+ APIResourceMgtClientException exception = null;
+ try {
+ apiResourceManager.addAPIResource(apiResource, tenantDomain);
+ } catch (APIResourceMgtClientException e) {
+ exception = e;
+ }
+ Assert.assertNull(exception);
+ Assert.assertNotNull(apiResourceManager.getAPIResourceByIdentifier(apiResource.getIdentifier(), tenantDomain));
+ }
+
+ @DataProvider(name = "addAPIResourceExceptionDataProvider")
+ public Object[][] addAPIResourceExceptionDataProvider() {
+
+ APIResource apiResource1 = createAPIResource("test1");
+
+ APIResource.APIResourceBuilder apiResourceBuilder = new APIResource.APIResourceBuilder()
+ .name("testAPIResource name 2")
+ .description("testAPIResource description 2");
+ APIResource apiResource2 = apiResourceBuilder.build();
+
+ return new Object[][]{
+ // Duplicate API resource.
+ {apiResource1},
+ // API resource without identifier.
+ {apiResource2}
+ };
+ }
+
+ @Test(dataProvider = "addAPIResourceExceptionDataProvider")
+ public void testAddAPIResourceException(APIResource apiResource) throws Exception {
+
+ addTestAPIResources();
+ APIResourceMgtException exception = null;
+ try {
+ apiResourceManager.addAPIResource(apiResource, tenantDomain);
+ } catch (APIResourceMgtException e) {
+ exception = e;
+ }
+ Assert.assertNotNull(exception);
+ }
+
+ @Test
+ public void testDeleteAPIResourceById() throws Exception {
+
+ APIResource createdAPIResource = apiResourceManager.addAPIResource(createAPIResource("test1"),
+ tenantDomain);
+ apiResourceManager.deleteAPIResourceById(createdAPIResource.getId(), tenantDomain);
+ Assert.assertNull(apiResourceManager.getAPIResourceById(createdAPIResource.getId(), tenantDomain));
+ }
+
+ @DataProvider
+ public Object[][] updateAPIResourceTestData() {
+
+ APIResource apiResource1 = createAPIResource("test1");
+ APIResource apiResource2 = createAPIResource("test2");
+
+ return new Object[][]{
+ // Update API resource with scopes.
+ {apiResource1, null, null, Arrays.asList(createScope("updated1"),
+ createScope("update2"))},
+ // Update API resource with name and description.
+ {apiResource2, "test2 updated name", "test2 updated description", null}
+ };
+ }
+
+ @Test(dataProvider = "updateAPIResourceTestData")
+ public void testUpdateAPIResource(APIResource apiResource, String updatedName, String updatedDescription,
+ List addedScopes) throws Exception {
+
+ APIResource createdAPIResource = apiResourceManager.addAPIResource(apiResource, tenantDomain);
+ APIResource.APIResourceBuilder apiResourceBuilder = new APIResource.APIResourceBuilder()
+ .id(createdAPIResource.getId())
+ .name(createdAPIResource.getName())
+ .description(createdAPIResource.getDescription())
+ .scopes(createdAPIResource.getScopes())
+ .type(createdAPIResource.getType())
+ .identifier(createdAPIResource.getIdentifier())
+ .requiresAuthorization(createdAPIResource.isRequiresAuthorization());
+
+ if (updatedName != null) {
+ apiResourceBuilder.name(updatedName);
+ }
+ if (updatedDescription != null) {
+ apiResourceBuilder.description(updatedDescription);
+ }
+ createdAPIResource = apiResourceBuilder.build();
+ apiResourceManager.updateAPIResource(createdAPIResource, addedScopes, null, tenantDomain);
+ APIResource updatedAPIResource = apiResourceManager.getAPIResourceById(createdAPIResource.getId(),
+ tenantDomain);
+ if (addedScopes != null) {
+ Assert.assertEquals(updatedAPIResource.getScopes().size(), 4);
+ }
+ if (updatedName != null) {
+ Assert.assertEquals(updatedAPIResource.getName(), updatedName);
+ }
+ if (updatedDescription != null) {
+ Assert.assertEquals(updatedAPIResource.getDescription(), updatedDescription);
+ }
+ }
+
+ @DataProvider(name = "getAPIResourceByIdentifierDataProvider")
+ public Object[][] getAPIResourceByIdentifierDataProvider() {
+
+ return new Object[][]{
+ {"testAPIResource identifier test1"}
+ };
+ }
+
+ @Test(dataProvider = "getAPIResourceByIdentifierDataProvider")
+ public void testGetAPIResourceByIdentifier(String identifier) throws Exception {
+
+ apiResourceManager.addAPIResource(createAPIResource("test1"), tenantDomain);
+ APIResource apiResource = apiResourceManager.getAPIResourceByIdentifier(identifier, tenantDomain);
+ Assert.assertNotNull(apiResource);
+ }
+
+ @Test
+ public void testGetAPIScopesById() throws Exception {
+
+ APIResource createdAPIResource = apiResourceManager.addAPIResource(createAPIResource("test1"),
+ tenantDomain);
+ List scopes = apiResourceManager.getAPIScopesById(createdAPIResource.getId(), tenantDomain);
+ Assert.assertNotNull(scopes);
+ }
+
+ @Test
+ public void testDeleteAPIScopesById() throws Exception {
+
+ APIResource createdAPIResource = apiResourceManager.addAPIResource(createAPIResource("test1"),
+ tenantDomain);
+ apiResourceManager.deleteAPIScopesById(createdAPIResource.getId(), tenantDomain);
+ List scopes = apiResourceManager.getAPIScopesById(createdAPIResource.getId(), tenantDomain);
+ Assert.assertTrue(scopes.isEmpty());
+ }
+
+ @Test
+ public void testDeleteAPIScopeByScopeId() throws Exception {
+
+ APIResource createdAPIResource = apiResourceManager.addAPIResource(createAPIResource("test1"),
+ tenantDomain);
+ List scopes = createdAPIResource.getScopes();
+ apiResourceManager.deleteAPIScopeByScopeName(createdAPIResource.getId(), scopes.get(0).getName(), tenantDomain);
+ scopes = apiResourceManager.getAPIScopesById(createdAPIResource.getId(), tenantDomain);
+ Assert.assertEquals(scopes.size(), 1);
+ }
+
+ @DataProvider(name = "putScopesDataProvider")
+ public Object[][] putScopesDataProvider() {
+
+ APIResource apiResource1 = createAPIResource("test1");
+ APIResource apiResource2 = createAPIResource("test2");
+
+ return new Object[][]{
+ // Update API resource with scopes.
+ {apiResource1, Arrays.asList(createScope("updated1"), createScope("update2"))},
+ // Update API resource with name and description.
+ {apiResource2, null}
+ };
+ }
+
+ @Test(dataProvider = "putScopesDataProvider")
+ public void testPutScopes(APIResource apiResource, List scopes) throws Exception {
+
+ APIResource createdAPIResource = apiResourceManager.addAPIResource(apiResource, tenantDomain);
+ apiResourceManager.putScopes(createdAPIResource.getId(), createdAPIResource.getScopes(), scopes, tenantDomain);
+ APIResource updatedAPIResource = apiResourceManager.getAPIResourceById(createdAPIResource.getId(),
+ tenantDomain);
+ if (scopes != null) {
+ Assert.assertEquals(updatedAPIResource.getScopes().size(), 2);
+ }
+ }
+
+ @DataProvider(name = "getScopesByTenantIdDataProvider")
+ public Object[][] getScopesByTenantIdDataProvider() {
+
+ return new Object[][]{
+ {null, 6},
+ {"name co 1", 2},
+ {"name eq testScopeOne test1", 1},
+ {"name sw test", 6}
+ };
+ }
+
+ @Test(dataProvider = "getScopesByTenantIdDataProvider")
+ public void testGetScopesByTenantId(String filter, int expected) throws Exception {
+
+ addTestAPIResources();
+ List scopes = apiResourceManager.getScopesByTenantDomain(tenantDomain, filter);
+ Assert.assertEquals(scopes.size(), expected);
+ }
+
+ /**
+ * Create scope with the given name.
+ *
+ * @param name Name of the scope.
+ * @return Scope.
+ */
+ private static Scope createScope(String name) {
+
+ Scope.ScopeBuilder scopeBuilder = new Scope.ScopeBuilder()
+ .name(name)
+ .displayName("displayName " + name)
+ .description("description " + name);
+ return scopeBuilder.build();
+ }
+
+ /**
+ * Create API resource with the given postfix.
+ *
+ * @param postFix Postfix to be appended to each API resource and scope information.
+ * @return API resource.
+ */
+ private static APIResource createAPIResource(String postFix) {
+
+ List scopes = new ArrayList<>();
+ scopes.add(createScope("testScopeOne " + postFix));
+ scopes.add(createScope("testScopeTwo " + postFix));
+
+ APIResource.APIResourceBuilder apiResourceBuilder = new APIResource.APIResourceBuilder()
+ .name("testAPIResource name " + postFix)
+ .identifier("testAPIResource identifier " + postFix)
+ .description("testAPIResource description " + postFix)
+ .type("BUSINESS")
+ .requiresAuthorization(true)
+ .scopes(scopes);
+ return apiResourceBuilder.build();
+ }
+
+ private void addTestAPIResources() throws Exception {
+
+ APIResource apiResource1 = createAPIResource("test1");
+ APIResource apiResource2 = createAPIResource("test2");
+ APIResource apiResource3 = createAPIResource("test3");
+ apiResourceManager.addAPIResource(apiResource1, tenantDomain);
+ apiResourceManager.addAPIResource(apiResource2, tenantDomain);
+ apiResourceManager.addAPIResource(apiResource3, tenantDomain);
+ }
+
+ private void removeTestAPIResources() throws Exception {
+
+ apiResourceManager.getAPIResources(null, null, 10, null, "ASC", tenantDomain)
+ .getAPIResources().forEach(
+ apiResource -> {
+ try {
+ apiResourceManager.deleteAPIResourceById(apiResource.getId(), tenantDomain);
+ } catch (APIResourceMgtException e) {
+ Assert.fail("Error while deleting API resource: " + apiResource.getIdentifier(), e);
+ }
+ }
+ );
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/java/org/wso2/carbon/identity/api/resource/mgt/dao/APIResourceManagementDAOImplTest.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/java/org/wso2/carbon/identity/api/resource/mgt/dao/APIResourceManagementDAOImplTest.java
new file mode 100644
index 000000000000..7cef342b3251
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/java/org/wso2/carbon/identity/api/resource/mgt/dao/APIResourceManagementDAOImplTest.java
@@ -0,0 +1,498 @@
+/*
+ * 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.api.resource.mgt.dao;
+
+import org.apache.commons.dbcp.BasicDataSource;
+import org.apache.commons.lang.StringUtils;
+import org.mockito.stubbing.Answer;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.testng.PowerMockTestCase;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import org.wso2.carbon.identity.api.resource.mgt.dao.impl.APIResourceManagementDAOImpl;
+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.Scope;
+import org.wso2.carbon.identity.core.model.ExpressionNode;
+import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil;
+import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
+import org.wso2.carbon.identity.core.util.IdentityUtil;
+
+import java.nio.file.Paths;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+@PrepareForTest({IdentityDatabaseUtil.class, IdentityTenantUtil.class, IdentityUtil.class, DataSource.class,
+ APIResourceManagementUtil.class})
+public class APIResourceManagementDAOImplTest extends PowerMockTestCase {
+
+ private static final int TENANT_ID = 2;
+ private static final int INVALID_TENANT_ID = 3;
+ private static final String DB_NAME = "api_resource_mgt_dao_db";
+ public static final String APIRESOURCE_IDENTIFIER = "testAPIResource identifier ";
+ public static final String TEST_SCOPE_1 = "testScope1 ";
+ private static final Map dataSourceMap = new HashMap<>();
+ private APIResourceManagementDAOImpl daoImpl;
+
+ @BeforeClass
+ public void setUp() throws Exception {
+
+ daoImpl = new APIResourceManagementDAOImpl();
+ initiateH2Database(getFilePath());
+ mockStatic(IdentityTenantUtil.class);
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+
+ // Add initial API resources.
+ addAPIResourceToDB("Setup-1", getConnection(), TENANT_ID);
+ addAPIResourceToDB("Setup-2", getConnection(), TENANT_ID);
+ }
+
+ @AfterClass
+ public void tearDown() throws Exception {
+
+ closeH2Database();
+ }
+
+ @DataProvider
+ public Object[][] getAPIResourcesCountData() {
+ return new Object[][]{
+ {TENANT_ID, new ArrayList<>(), 2},
+ {INVALID_TENANT_ID, new ArrayList<>(), 0},
+ };
+ }
+
+ @Test(dataProvider = "getAPIResourcesCountData")
+ public void testGetAPIResourcesCount(Integer tenantId, List expressionNodes, int expected)
+ throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertEquals(daoImpl.getAPIResourcesCount(tenantId, expressionNodes).intValue(), expected);
+ }
+
+ @DataProvider
+ public Object[][] getAPIResourcesData() {
+ return new Object[][]{
+ {2, TENANT_ID, "ASC", new ArrayList<>(), 2},
+ {1, TENANT_ID, "ASC", new ArrayList<>(), 1},
+ {1, INVALID_TENANT_ID, "ASC", new ArrayList<>(), 0},
+ };
+ }
+
+ @Test(dataProvider = "getAPIResourcesData", priority = 1)
+ public void testGetAPIResources(Integer limit, Integer tenantId, String sortOrder,
+ List expressionNodes, int count) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertEquals(daoImpl.getAPIResources(limit, tenantId, sortOrder, expressionNodes).size(), count);
+ }
+
+ @DataProvider
+ public Object[][] addAPIResourceData() {
+
+ return new Object[][]{
+ {"AddAPITest-1", TENANT_ID}
+ };
+ }
+
+ @Test(dataProvider = "addAPIResourceData", priority = 2)
+ public void testAddAPIResource(String postfix, int tenantId) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ APIResource apiResource = createAPIResource(postfix);
+ APIResource createdAPIResource = daoImpl.addAPIResource(apiResource, tenantId);
+ Assert.assertNotNull(createdAPIResource);
+ Assert.assertTrue(createdAPIResource.getName().contains(postfix));
+ Assert.assertNotNull(createdAPIResource.getId());
+ }
+
+ @DataProvider
+ public Object[][] getScopesByAPIData() {
+ // Define your test cases here
+ return new Object[][]{
+ {"GetScopesTest", TENANT_ID, 2},
+ {"GetScopesTest2", INVALID_TENANT_ID, 0}
+ };
+ }
+
+ @Test(dataProvider = "getScopesByAPIData", priority = 3)
+ public void testGetScopesByAPI(String name, Integer tenantId, int expected) throws Exception {
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+
+ String apiId = addAPIResourceToDB(name, getConnection(), tenantId).getId();
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertEquals(daoImpl.getScopesByAPI(apiId, TENANT_ID).size(), expected);
+ }
+
+ @DataProvider
+ public Object[][] isAPIResourceExistData() {
+ return new Object[][]{
+ {"identifier1", TENANT_ID, true},
+ {"identifier4", INVALID_TENANT_ID, false}
+ };
+ }
+
+ @Test(dataProvider = "isAPIResourceExistData", priority = 4)
+ public void testIsAPIResourceExist(String identifierPostFix, Integer tenantId, boolean expected) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ addAPIResourceToDB(identifierPostFix, getConnection(), tenantId);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertEquals(daoImpl.isAPIResourceExist(APIRESOURCE_IDENTIFIER + identifierPostFix, TENANT_ID),
+ expected);
+ }
+
+ @DataProvider
+ public Object[][] isAPIResourceExistByIdData() {
+ return new Object[][]{
+ {TENANT_ID, true},
+ {INVALID_TENANT_ID, false}
+ };
+ }
+
+ @Test(dataProvider = "isAPIResourceExistByIdData", priority = 5)
+ public void testIsAPIResourceExistById(Integer tenantId, boolean expected) throws Exception {
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ APIResource createdAPIResource = addAPIResourceToDB("testIsAPIResourceExistById", getConnection(),
+ tenantId);
+ String apiId = createdAPIResource.getId();
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertEquals(daoImpl.isAPIResourceExistById(apiId, TENANT_ID), expected);
+ }
+
+ @DataProvider
+ public Object[][] getAPIResourceByIdData() {
+ return new Object[][]{
+ {TENANT_ID, true},
+ {INVALID_TENANT_ID, false}
+ };
+ }
+
+ @Test(dataProvider = "getAPIResourceByIdData", priority = 6)
+ public void testGetAPIResourceById(Integer tenantId, boolean expected) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ APIResource createdAPIResource = addAPIResourceToDB("testGetAPIResourceById", getConnection(),
+ tenantId);
+ String apiId = createdAPIResource.getId();
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertEquals(daoImpl.getAPIResourceById(apiId, TENANT_ID) != null, expected);
+ }
+
+ @DataProvider
+ public Object[][] isScopeExistByIdData() {
+ return new Object[][]{
+ {TENANT_ID, true},
+ {INVALID_TENANT_ID, false}
+ };
+ }
+
+ @Test(dataProvider = "isScopeExistByIdData", priority = 7)
+ public void testIsScopeExistById(Integer tenantId, boolean expected) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ APIResource createdAPIResource = addAPIResourceToDB("testIsScopeExistById", getConnection(),
+ tenantId);
+ String scopeId = createdAPIResource.getScopes().get(0).getId();
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertEquals(daoImpl.isScopeExistById(scopeId, TENANT_ID), expected);
+ }
+
+ @DataProvider
+ public Object[][] deleteAPIResourceByIdData() {
+
+ return new Object[][]{
+ {TENANT_ID, false}
+ };
+ }
+
+ @Test(dataProvider = "deleteAPIResourceByIdData", priority = 8)
+ public void testDeleteAPIResourceById(Integer tenantId, boolean expected) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ String apiId = addAPIResourceToDB("testDeleteAPIResourceById", getConnection(), tenantId).getId();
+ Connection connection = getConnection();
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(connection);
+ PowerMockito.doAnswer((Answer) invocation -> {
+ connection.commit();
+ return null;
+ }).when(IdentityDatabaseUtil.class, "commitTransaction", any(Connection.class));
+ daoImpl.deleteAPIResourceById(apiId, tenantId);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertEquals(daoImpl.isAPIResourceExistById(apiId, tenantId), expected);
+ }
+
+ @DataProvider
+ public Object[][] isScopeExistByNameData() {
+ return new Object[][]{
+ {TENANT_ID, "testIsScopeExistByName", true},
+ {INVALID_TENANT_ID, "nonExistentScopeName", false}
+ };
+ }
+
+ @Test(dataProvider = "isScopeExistByNameData", priority = 9)
+ public void testIsScopeExistByName(Integer tenantId, String scopeName, boolean expected) throws Exception {
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ addAPIResourceToDB(scopeName, getConnection(), tenantId);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+
+ Assert.assertEquals(daoImpl.isScopeExistByName(TEST_SCOPE_1 + scopeName, TENANT_ID), expected);
+ }
+
+ @DataProvider
+ public Object[][] getScopeByNameAndTenantIdData() {
+ String scopeName = "testGetScopeByNameAndTenantId";
+ return new Object[][]{
+ {TENANT_ID, scopeName, scopeName}
+ };
+ }
+
+ @Test(dataProvider = "getScopeByNameAndTenantIdData", priority = 10)
+ public void testGetScopeByNameAndTenantId(Integer tenantId, String scopeName, String expectedName)
+ throws Exception {
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ addAPIResourceToDB(scopeName, getConnection(), tenantId);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Scope scope = daoImpl.getScopeByNameAndTenantId(TEST_SCOPE_1 + scopeName, tenantId);
+ Assert.assertEquals(scope.getName(), TEST_SCOPE_1 + expectedName);
+ }
+
+ @Test(priority = 11)
+ public void testAddScopes() throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ APIResource apiResource = addAPIResourceToDB("testAddScopes", getConnection(), TENANT_ID);
+ String apiId = apiResource.getId();
+ List scopes = Arrays.asList(createScope("scope1"), createScope("scope2"));
+
+ Connection connection = getConnection();
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(connection);
+ PowerMockito.doAnswer((Answer) invocation -> {
+ connection.commit();
+ return null;
+ }).when(IdentityDatabaseUtil.class, "commitTransaction", any(Connection.class));
+ daoImpl.addScopes(scopes, apiId, TENANT_ID);
+
+ for (Scope scope : scopes) {
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertTrue(daoImpl.isScopeExistByName(scope.getName(), TENANT_ID));
+ }
+ }
+
+ @Test(priority = 12)
+ public void testDeleteAllScopes() throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ APIResource apiResource = addAPIResourceToDB("testDeleteAllScopes", getConnection(), TENANT_ID);
+ String apiId = apiResource.getId();
+ Connection connection = getConnection();
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(connection);
+ PowerMockito.doAnswer((Answer) invocation -> {
+ connection.commit();
+ return null;
+ }).when(IdentityDatabaseUtil.class, "commitTransaction", any(Connection.class));
+
+ daoImpl.deleteAllScopes(apiId, TENANT_ID);
+ for (Scope scope : apiResource.getScopes()) {
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertFalse(daoImpl.isScopeExistById(scope.getId(), TENANT_ID));
+ }
+ }
+
+ @Test(priority = 13)
+ public void testDeleteScope() throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ APIResource apiResource = addAPIResourceToDB("testDeleteScope", getConnection(), TENANT_ID);
+ String apiId = apiResource.getId();
+ String scopeName = apiResource.getScopes().get(0).getName(); // Assuming there's at least one scope
+
+ Connection connection = getConnection();
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(connection);
+ PowerMockito.doAnswer((Answer) invocation -> {
+ connection.commit();
+ return null;
+ }).when(IdentityDatabaseUtil.class, "commitTransaction", any(Connection.class));
+
+ // Testing the deleteScope method with the created API resource's ID and scope ID
+ daoImpl.deleteScope(apiId, scopeName, TENANT_ID);
+
+ // Checking whether the scope is deleted
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertFalse(daoImpl.isScopeExistByName(scopeName, TENANT_ID));
+ }
+
+ /**
+ * Create scope with the given name.
+ *
+ * @param name Name of the scope.
+ * @return Scope.
+ */
+ private static Scope createScope(String name) {
+
+ Scope.ScopeBuilder scopeBuilder = new Scope.ScopeBuilder()
+ .name(name)
+ .displayName("displayName " + name)
+ .description("description " + name);
+ return scopeBuilder.build();
+ }
+
+
+ /**
+ * Create API resource with the given postfix.
+ *
+ * @param postFix Postfix to be appended to each API resource and scope information.
+ * @return API resource.
+ */
+ private static APIResource createAPIResource(String postFix) {
+
+ List scopes = new ArrayList<>();
+ scopes.add(createScope(TEST_SCOPE_1 + postFix));
+ scopes.add(createScope("testScope2 " + postFix));
+
+ APIResource.APIResourceBuilder apiResourceBuilder = new APIResource.APIResourceBuilder()
+ .name("testAPIResource name " + postFix)
+ .identifier(APIRESOURCE_IDENTIFIER + postFix)
+ .description("testAPIResource description " + postFix)
+ .type("BUSINESS")
+ .requiresAuthorization(true)
+ .scopes(scopes);
+ return apiResourceBuilder.build();
+ }
+
+ /**
+ * Add API resource to the database.
+ *
+ * @param namePostFix Postfix to be appended to each API resource and scope information.
+ * @param connection Database connection.
+ * @return API resource.
+ * @throws Exception Error when adding API resource.
+ */
+ private APIResource addAPIResourceToDB(String namePostFix, Connection connection, int tenantId) throws Exception {
+
+ APIResource apiResource = createAPIResource(namePostFix);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(connection);
+ PowerMockito.doAnswer((Answer) invocation -> {
+ connection.commit();
+ return null;
+ }).when(IdentityDatabaseUtil.class, "commitTransaction", any(Connection.class));
+ return daoImpl.addAPIResource(apiResource, tenantId);
+ }
+
+ private static Connection getConnection() throws SQLException {
+
+ if (dataSourceMap.get(DB_NAME) != null) {
+ return dataSourceMap.get(DB_NAME).getConnection();
+ }
+ throw new RuntimeException("No datasource initiated for database: " + APIResourceManagementDAOImplTest.DB_NAME);
+ }
+
+ /**
+ * Initiate H2 database.
+ *
+ * @param scriptPath Path to the database script.
+ * @throws Exception Error when initiating H2 database.
+ */
+ private void initiateH2Database(String scriptPath) throws Exception {
+
+ BasicDataSource dataSource = new BasicDataSource();
+ dataSource.setDriverClassName("org.h2.Driver");
+ dataSource.setUsername("username");
+ dataSource.setPassword("password");
+ dataSource.setUrl("jdbc:h2:mem:test" + DB_NAME);
+ dataSource.setTestOnBorrow(true);
+ dataSource.setValidationQuery("select 1");
+ try (Connection connection = dataSource.getConnection()) {
+ connection.createStatement().executeUpdate("RUNSCRIPT FROM '" + scriptPath + "'");
+ }
+ dataSourceMap.put(DB_NAME, dataSource);
+ }
+
+ /**
+ * Close H2 database.
+ *
+ * @throws Exception Error when closing H2 database.
+ */
+ public static void closeH2Database() throws Exception {
+
+ BasicDataSource dataSource = dataSourceMap.get(DB_NAME);
+ if (dataSource != null) {
+ dataSource.close();
+ }
+ }
+
+ /**
+ * Get the path to the database script.
+ *
+ * @return Path to the database script.
+ */
+ private static String getFilePath() {
+
+ if (StringUtils.isNotBlank("h2.sql")) {
+ return Paths.get(System.getProperty("user.dir"), "src", "test", "resources", "dbscripts", "h2.sql")
+ .toString();
+ }
+ throw new IllegalArgumentException("DB Script file name cannot be empty.");
+ }
+}
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/java/org/wso2/carbon/identity/api/resource/mgt/dao/CacheBackedAPIResourceManagementDAOTest.java b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/java/org/wso2/carbon/identity/api/resource/mgt/dao/CacheBackedAPIResourceManagementDAOTest.java
new file mode 100644
index 000000000000..dfb4525779f6
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/java/org/wso2/carbon/identity/api/resource/mgt/dao/CacheBackedAPIResourceManagementDAOTest.java
@@ -0,0 +1,557 @@
+/*
+ * 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.api.resource.mgt.dao;
+
+import org.apache.commons.dbcp.BasicDataSource;
+import org.apache.commons.lang.StringUtils;
+import org.mockito.stubbing.Answer;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.testng.PowerMockTestCase;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import org.wso2.carbon.base.CarbonBaseConstants;
+import org.wso2.carbon.identity.api.resource.mgt.dao.impl.APIResourceManagementDAOImpl;
+import org.wso2.carbon.identity.api.resource.mgt.dao.impl.CacheBackedAPIResourceMgtDAO;
+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.Scope;
+import org.wso2.carbon.identity.core.model.ExpressionNode;
+import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil;
+import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
+import org.wso2.carbon.identity.core.util.IdentityUtil;
+
+import java.nio.file.Paths;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+@PrepareForTest({IdentityDatabaseUtil.class, IdentityTenantUtil.class, IdentityUtil.class, DataSource.class,
+ APIResourceManagementUtil.class})
+public class CacheBackedAPIResourceManagementDAOTest extends PowerMockTestCase {
+
+ private static final int TENANT_ID = 2;
+ private static final String TENANT_DOMAIN = "carbon.super";
+ private static final int INVALID_TENANT_ID = 3;
+ private static final String INVALID_TENANT_DOMAIN = "invalid.tenant";
+ private static final String DB_NAME = "cache_backed_api_resource_mgt_dao_db";
+ public static final String API_RESOURCE_IDENTIFIER = "testAPIResource identifier ";
+ public static final String TEST_SCOPE_1 = "testScope1 ";
+ private static final Map dataSourceMap = new HashMap<>();
+ private CacheBackedAPIResourceMgtDAO daoImpl;
+
+ @BeforeClass
+ public void setUp() throws Exception {
+
+ setUpCarbonHome();
+ APIResourceManagementDAOImpl apiResourceManagementDAO = new APIResourceManagementDAOImpl();
+ daoImpl = new CacheBackedAPIResourceMgtDAO(apiResourceManagementDAO);
+ initiateH2Database(getFilePath());
+ mockStatic(IdentityTenantUtil.class);
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+
+ // Add initial API resources.
+ addAPIResourceToDB("Setup-1", getConnection(), TENANT_ID);
+ addAPIResourceToDB("Setup-2", getConnection(), TENANT_ID);
+ }
+
+ private static void setUpCarbonHome() {
+
+ String carbonHome = Paths.get(System.getProperty("user.dir"), "target", "test-classes").toString();
+ System.setProperty(CarbonBaseConstants.CARBON_HOME, carbonHome);
+ System.setProperty(CarbonBaseConstants.CARBON_CONFIG_DIR_PATH, Paths.get(carbonHome,
+ "repository/conf").toString());
+ }
+
+ @AfterClass
+ public void tearDown() throws Exception {
+
+ closeH2Database();
+ }
+
+ @DataProvider
+ public Object[][] getAPIResourcesCountData() {
+ return new Object[][]{
+ {TENANT_ID, new ArrayList<>(), 2},
+ {INVALID_TENANT_ID, new ArrayList<>(), 0},
+ };
+ }
+
+ @Test(dataProvider = "getAPIResourcesCountData")
+ public void testGetAPIResourcesCount(Integer tenantId, List expressionNodes, int expected)
+ throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertEquals(daoImpl.getAPIResourcesCount(tenantId, expressionNodes).intValue(), expected);
+ }
+
+ @DataProvider
+ public Object[][] getAPIResourcesData() {
+ return new Object[][]{
+ {2, TENANT_ID, "ASC", new ArrayList<>(), 2},
+ {1, TENANT_ID, "ASC", new ArrayList<>(), 1},
+ {1, INVALID_TENANT_ID, "ASC", new ArrayList<>(), 0},
+ };
+ }
+
+ @Test(dataProvider = "getAPIResourcesData", priority = 1)
+ public void testGetAPIResources(Integer limit, Integer tenantId, String sortOrder,
+ List expressionNodes, int count) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertEquals(daoImpl.getAPIResources(limit, tenantId, sortOrder, expressionNodes).size(), count);
+ }
+
+ @DataProvider
+ public Object[][] addAPIResourceData() {
+
+ return new Object[][]{
+ {"AddAPITest-1", TENANT_ID}
+ };
+ }
+
+ @Test(dataProvider = "addAPIResourceData", priority = 2)
+ public void testAddAPIResource(String postfix, int tenantId) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ APIResource apiResource = createAPIResource(postfix);
+ APIResource createdAPIResource = daoImpl.addAPIResource(apiResource, tenantId);
+ Assert.assertNotNull(createdAPIResource);
+ Assert.assertTrue(createdAPIResource.getName().contains(postfix));
+ Assert.assertNotNull(createdAPIResource.getId());
+ }
+
+ @DataProvider
+ public Object[][] getScopesByAPIData() {
+ // Define your test cases here
+ return new Object[][]{
+ {"GetScopesTest", TENANT_ID, 2},
+ {"GetScopesTest", INVALID_TENANT_ID, 0}
+ };
+ }
+
+ @Test(dataProvider = "getScopesByAPIData", priority = 3)
+ public void testGetScopesByAPI(String name, Integer tenantId, int expected) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(IdentityTenantUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ String apiId = addAPIResourceToDB(name, getConnection(), tenantId).getId();
+ when(IdentityDatabaseUtil.getDBConnection(true)).thenReturn(getConnection());
+ when(IdentityDatabaseUtil.getDBConnection(false)).thenReturn(getConnection());
+ when(IdentityTenantUtil.getTenantDomain(TENANT_ID)).thenReturn(getTenantDomain(TENANT_ID));
+ Assert.assertEquals(daoImpl.getScopesByAPI(apiId, TENANT_ID).size(), expected);
+ }
+
+ @DataProvider
+ public Object[][] isAPIResourceExistData() {
+ return new Object[][]{
+ {"identifier1", TENANT_ID, true},
+ {"identifier4", INVALID_TENANT_ID, false}
+ };
+ }
+
+ @Test(dataProvider = "isAPIResourceExistData", priority = 4)
+ public void testIsAPIResourceExist(String identifierPostFix, Integer tenantId, boolean expected) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(IdentityTenantUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ addAPIResourceToDB(identifierPostFix, getConnection(), tenantId);
+ when(IdentityDatabaseUtil.getDBConnection(true)).thenReturn(getConnection());
+ when(IdentityDatabaseUtil.getDBConnection(false)).thenReturn(getConnection());
+ when(IdentityTenantUtil.getTenantDomain(TENANT_ID)).thenReturn(getTenantDomain(TENANT_ID));
+ Assert.assertEquals(daoImpl.isAPIResourceExist(API_RESOURCE_IDENTIFIER + identifierPostFix, TENANT_ID),
+ expected);
+ }
+
+ @DataProvider
+ public Object[][] isAPIResourceExistByIdData() {
+ return new Object[][]{
+ {TENANT_ID, true},
+ {INVALID_TENANT_ID, false}
+ };
+ }
+
+ @Test(dataProvider = "isAPIResourceExistByIdData", priority = 5)
+ public void testIsAPIResourceExistById(Integer tenantId, boolean expected) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(IdentityTenantUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ APIResource apiresource = addAPIResourceToDB("testIsAPIResourceExistById", getConnection(), tenantId);
+ String apiId = apiresource.getId();
+ when(IdentityDatabaseUtil.getDBConnection(true)).thenReturn(getConnection());
+ when(IdentityDatabaseUtil.getDBConnection(false)).thenReturn(getConnection());
+ when(IdentityTenantUtil.getTenantDomain(TENANT_ID)).thenReturn(getTenantDomain(TENANT_ID));
+ Assert.assertEquals(daoImpl.isAPIResourceExistById(apiId, TENANT_ID), expected);
+ }
+
+ @DataProvider
+ public Object[][] getAPIResourceByIdData() {
+ return new Object[][]{
+ {TENANT_ID, true},
+ {INVALID_TENANT_ID, false}
+ };
+ }
+
+ @Test(dataProvider = "getAPIResourceByIdData", priority = 6)
+ public void testGetAPIResourceById(Integer tenantId, boolean expected) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+ mockStatic(IdentityTenantUtil.class);
+ when(IdentityTenantUtil.getTenantDomain(tenantId)).thenReturn(getTenantDomain(tenantId));
+
+ String apiId = addAPIResourceToDB("testGetAPIResourceById", getConnection(), tenantId).getId();
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+
+ when(IdentityTenantUtil.getTenantDomain(TENANT_ID)).thenReturn(getTenantDomain(TENANT_ID));
+
+ Assert.assertEquals(daoImpl.getAPIResourceById(apiId, TENANT_ID) != null, expected);
+ }
+
+ @DataProvider
+ public Object[][] isScopeExistByIdData() {
+ return new Object[][]{
+ {TENANT_ID, true},
+ {INVALID_TENANT_ID, false}
+ };
+ }
+
+ @Test(dataProvider = "isScopeExistByIdData", priority = 7)
+ public void testIsScopeExistById(Integer tenantId, boolean expected) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ String scopeId = addAPIResourceToDB("testIsScopeExistById", getConnection(), tenantId)
+ .getScopes().get(0).getId();
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Assert.assertEquals(daoImpl.isScopeExistById(scopeId, TENANT_ID), expected);
+ }
+
+ @DataProvider
+ public Object[][] deleteAPIResourceByIdData() {
+
+ return new Object[][]{
+ {TENANT_ID, false}
+ };
+ }
+
+ @Test(dataProvider = "deleteAPIResourceByIdData", priority = 8)
+ public void testDeleteAPIResourceById(Integer tenantId, boolean expected) throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+ mockStatic(IdentityTenantUtil.class);
+ when(IdentityTenantUtil.getTenantDomain(tenantId)).thenReturn(getTenantDomain(tenantId));
+
+ String apiId = addAPIResourceToDB("testDeleteAPIResourceById", getConnection(), tenantId).getId();
+ Connection connection = getConnection();
+ when(IdentityDatabaseUtil.getDBConnection(true)).thenReturn(connection);
+ when(IdentityDatabaseUtil.getDBConnection(false)).thenReturn(getConnection());
+
+ PowerMockito.doAnswer((Answer) invocation -> {
+ connection.commit();
+ return null;
+ }).when(IdentityDatabaseUtil.class, "commitTransaction", any(Connection.class));
+
+ daoImpl.deleteAPIResourceById(apiId, tenantId);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ when(IdentityTenantUtil.getTenantDomain(tenantId)).thenReturn(getTenantDomain(tenantId));
+ Assert.assertEquals(daoImpl.isAPIResourceExistById(apiId, tenantId), expected);
+ }
+
+ @DataProvider
+ public Object[][] isScopeExistByNameData() {
+ return new Object[][]{
+ {TENANT_ID, "testIsScopeExistByName", true},
+ {INVALID_TENANT_ID, "nonExistentScopeName", false}
+ };
+ }
+
+ @Test(dataProvider = "isScopeExistByNameData", priority = 9)
+ public void testIsScopeExistByName(Integer tenantId, String scopeName, boolean expected) throws Exception {
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ addAPIResourceToDB(scopeName, getConnection(), tenantId);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+
+ Assert.assertEquals(daoImpl.isScopeExistByName(TEST_SCOPE_1 + scopeName, TENANT_ID), expected);
+ }
+
+ @DataProvider
+ public Object[][] getScopeByNameAndTenantIdData() {
+ String scopeName = "testGetScopeByNameAndTenantId";
+ return new Object[][]{
+ {TENANT_ID, scopeName, scopeName}
+ };
+ }
+
+ @Test(dataProvider = "getScopeByNameAndTenantIdData", priority = 10)
+ public void testGetScopeByNameAndTenantId(Integer tenantId, String scopeName, String expectedName)
+ throws Exception {
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+
+ addAPIResourceToDB(scopeName, getConnection(), tenantId);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ Scope scope = daoImpl.getScopeByNameAndTenantId(TEST_SCOPE_1 + scopeName, tenantId);
+ Assert.assertEquals(scope.getName(), TEST_SCOPE_1 + expectedName);
+ }
+
+ @Test(priority = 11)
+ public void testAddScopes() throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+ mockStatic(IdentityTenantUtil.class);
+ when(IdentityTenantUtil.getTenantDomain(TENANT_ID)).thenReturn(getTenantDomain(TENANT_ID));
+
+ APIResource apiResource = addAPIResourceToDB("testAddScopes", getConnection(), TENANT_ID);
+ String apiId = apiResource.getId();
+ List scopes = Arrays.asList(createScope("scope1"), createScope("scope2"));
+
+ Connection connection = getConnection();
+ when(IdentityDatabaseUtil.getDBConnection(true)).thenReturn(connection);
+ when(IdentityDatabaseUtil.getDBConnection(false)).thenReturn(getConnection());
+
+ PowerMockito.doAnswer((Answer) invocation -> {
+ connection.commit();
+ return null;
+ }).when(IdentityDatabaseUtil.class, "commitTransaction", any(Connection.class));
+
+ daoImpl.addScopes(scopes, apiId, TENANT_ID);
+
+ for (Scope scope : scopes) {
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ when(IdentityTenantUtil.getTenantDomain(TENANT_ID)).thenReturn(getTenantDomain(TENANT_ID));
+ Assert.assertTrue(daoImpl.isScopeExistByName(scope.getName(), TENANT_ID));
+ }
+ }
+
+ @Test(priority = 12)
+ public void testDeleteAllScopes() throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+ mockStatic(IdentityTenantUtil.class);
+ when(IdentityTenantUtil.getTenantDomain(TENANT_ID)).thenReturn(getTenantDomain(TENANT_ID));
+
+ APIResource apiResource = addAPIResourceToDB("testDeleteAllScopes", getConnection(), TENANT_ID);
+ String apiId = apiResource.getId();
+
+ Connection connection = getConnection();
+ when(IdentityDatabaseUtil.getDBConnection(true)).thenReturn(connection);
+ when(IdentityDatabaseUtil.getDBConnection(false)).thenReturn(getConnection());
+
+ PowerMockito.doAnswer((Answer) invocation -> {
+ connection.commit();
+ return null;
+ }).when(IdentityDatabaseUtil.class, "commitTransaction", any(Connection.class));
+
+ daoImpl.deleteAllScopes(apiId, TENANT_ID);
+ for (Scope scope : apiResource.getScopes()) {
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ when(IdentityTenantUtil.getTenantDomain(TENANT_ID)).thenReturn(getTenantDomain(TENANT_ID));
+ Assert.assertFalse(daoImpl.isScopeExistById(scope.getId(), TENANT_ID));
+ }
+ }
+
+ @Test(priority = 13)
+ public void testDeleteScope() throws Exception {
+
+ mockStatic(IdentityDatabaseUtil.class);
+ mockStatic(APIResourceManagementUtil.class);
+ mockStatic(IdentityTenantUtil.class);
+ when(IdentityTenantUtil.getTenantDomain(TENANT_ID)).thenReturn(getTenantDomain(TENANT_ID));
+
+ APIResource apiResource = addAPIResourceToDB("testDeleteScope", getConnection(), TENANT_ID);
+ String apiId = apiResource.getId();
+ String scopeName = apiResource.getScopes().get(0).getName();
+
+ Connection connection = getConnection();
+ when(IdentityDatabaseUtil.getDBConnection(true)).thenReturn(connection);
+ when(IdentityDatabaseUtil.getDBConnection(false)).thenReturn(getConnection());
+
+ PowerMockito.doAnswer((Answer) invocation -> {
+ connection.commit();
+ return null;
+ }).when(IdentityDatabaseUtil.class, "commitTransaction", any(Connection.class));
+
+ // Testing the deleteScope method with the created API resource's ID and scope ID
+ daoImpl.deleteScope(apiId, scopeName, TENANT_ID);
+
+ // Checking whether the scope is deleted
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(getConnection());
+ when(IdentityTenantUtil.getTenantDomain(TENANT_ID)).thenReturn(getTenantDomain(TENANT_ID));
+ Assert.assertFalse(daoImpl.isScopeExistByName(scopeName, TENANT_ID));
+ }
+
+ /**
+ * Create scope with the given name.
+ *
+ * @param name Name of the scope.
+ * @return Scope.
+ */
+ private static Scope createScope(String name) {
+
+ Scope.ScopeBuilder scopeBuilder = new Scope.ScopeBuilder()
+ .name(name)
+ .displayName("displayName " + name)
+ .description("description " + name);
+ return scopeBuilder.build();
+ }
+
+
+ /**
+ * Create API resource with the given postfix.
+ *
+ * @param postFix Postfix to be appended to each API resource and scope information.
+ * @return API resource.
+ */
+ private static APIResource createAPIResource(String postFix) {
+
+ List scopes = new ArrayList<>();
+ scopes.add(createScope(TEST_SCOPE_1 + postFix));
+ scopes.add(createScope("testScope2 " + postFix));
+
+ APIResource.APIResourceBuilder apiResourceBuilder = new APIResource.APIResourceBuilder()
+ .name("testAPIResource name " + postFix)
+ .identifier(API_RESOURCE_IDENTIFIER + postFix)
+ .description("testAPIResource description " + postFix)
+ .type("BUSINESS")
+ .requiresAuthorization(true)
+ .scopes(scopes);
+ return apiResourceBuilder.build();
+ }
+
+ /**
+ * Add API resource to the database.
+ *
+ * @param namePostFix Postfix to be appended to each API resource and scope information.
+ * @param connection Database connection.
+ * @return API resource.
+ * @throws Exception Error when adding API resource.
+ */
+ private APIResource addAPIResourceToDB(String namePostFix, Connection connection, int tenantId) throws Exception {
+
+ APIResource apiResource = createAPIResource(namePostFix);
+ when(IdentityDatabaseUtil.getDBConnection(anyBoolean())).thenReturn(connection);
+ PowerMockito.doAnswer((Answer) invocation -> {
+ connection.commit();
+ return null;
+ }).when(IdentityDatabaseUtil.class, "commitTransaction", any(Connection.class));
+ return daoImpl.addAPIResource(apiResource, tenantId);
+ }
+
+ private static Connection getConnection() throws SQLException {
+
+ if (dataSourceMap.get(DB_NAME) != null) {
+ return dataSourceMap.get(DB_NAME).getConnection();
+ }
+ throw new RuntimeException("No datasource initiated for database: " +
+ CacheBackedAPIResourceManagementDAOTest.DB_NAME);
+ }
+
+ /**
+ * Initiate H2 database.
+ *
+ * @param scriptPath Path to the database script.
+ * @throws Exception Error when initiating H2 database.
+ */
+ private void initiateH2Database(String scriptPath) throws Exception {
+
+ BasicDataSource dataSource = new BasicDataSource();
+ dataSource.setDriverClassName("org.h2.Driver");
+ dataSource.setUsername("username");
+ dataSource.setPassword("password");
+ dataSource.setUrl("jdbc:h2:mem:test" + DB_NAME);
+ dataSource.setTestOnBorrow(true);
+ dataSource.setValidationQuery("select 1");
+ try (Connection connection = dataSource.getConnection()) {
+ connection.createStatement().executeUpdate("RUNSCRIPT FROM '" + scriptPath + "'");
+ }
+ dataSourceMap.put(DB_NAME, dataSource);
+ }
+
+ /**
+ * Close H2 database.
+ *
+ * @throws Exception Error when closing H2 database.
+ */
+ public static void closeH2Database() throws Exception {
+
+ BasicDataSource dataSource = dataSourceMap.get(DB_NAME);
+ if (dataSource != null) {
+ dataSource.close();
+ }
+ }
+
+ /**
+ * Get the path to the database script.
+ *
+ * @return Path to the database script.
+ */
+ private static String getFilePath() {
+
+ if (StringUtils.isNotBlank("h2.sql")) {
+ return Paths.get(System.getProperty("user.dir"), "src", "test", "resources", "dbscripts", "h2.sql")
+ .toString();
+ }
+ throw new IllegalArgumentException("DB Script file name cannot be empty.");
+ }
+
+ private static String getTenantDomain(int tenantId) {
+
+ if (tenantId == TENANT_ID) {
+ return TENANT_DOMAIN;
+ } else if (tenantId == INVALID_TENANT_ID) {
+ return INVALID_TENANT_DOMAIN;
+ }
+ return null;
+ }
+}
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
new file mode 100644
index 000000000000..3216b8d0d296
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/dbscripts/h2.sql
@@ -0,0 +1,1391 @@
+CREATE TABLE IF NOT EXISTS IDN_BASE_TABLE (
+ PRODUCT_NAME VARCHAR (20),
+ PRIMARY KEY (PRODUCT_NAME)
+);
+
+INSERT INTO IDN_BASE_TABLE values ('WSO2 Identity Server');
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH_CONSUMER_APPS (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ CONSUMER_KEY VARCHAR (255),
+ CONSUMER_SECRET VARCHAR (2048),
+ USERNAME VARCHAR (255),
+ TENANT_ID INTEGER DEFAULT 0,
+ USER_DOMAIN VARCHAR(50),
+ APP_NAME VARCHAR (255),
+ OAUTH_VERSION VARCHAR (128),
+ CALLBACK_URL VARCHAR (2048),
+ GRANT_TYPES VARCHAR (1024),
+ PKCE_MANDATORY CHAR(1) DEFAULT '0',
+ PKCE_SUPPORT_PLAIN CHAR(1) DEFAULT '0',
+ APP_STATE VARCHAR (25) DEFAULT 'ACTIVE',
+ USER_ACCESS_TOKEN_EXPIRE_TIME BIGINT DEFAULT 3600,
+ APP_ACCESS_TOKEN_EXPIRE_TIME BIGINT DEFAULT 3600,
+ REFRESH_TOKEN_EXPIRE_TIME BIGINT DEFAULT 84600,
+ ID_TOKEN_EXPIRE_TIME BIGINT DEFAULT 3600,
+ CONSTRAINT CONSUMER_KEY_CONSTRAINT UNIQUE (CONSUMER_KEY),
+ PRIMARY KEY (ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_SCOPE_VALIDATORS (
+ APP_ID INTEGER NOT NULL,
+ SCOPE_VALIDATOR VARCHAR (128) NOT NULL,
+ PRIMARY KEY (APP_ID,SCOPE_VALIDATOR),
+ FOREIGN KEY (APP_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH1A_REQUEST_TOKEN (
+ REQUEST_TOKEN VARCHAR (512),
+ REQUEST_TOKEN_SECRET VARCHAR (512),
+ CONSUMER_KEY_ID INTEGER,
+ CALLBACK_URL VARCHAR (2048),
+ SCOPE VARCHAR(2048),
+ AUTHORIZED VARCHAR (128),
+ OAUTH_VERIFIER VARCHAR (512),
+ AUTHZ_USER VARCHAR (512),
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY (REQUEST_TOKEN),
+ FOREIGN KEY (CONSUMER_KEY_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH1A_ACCESS_TOKEN (
+ ACCESS_TOKEN VARCHAR (512),
+ ACCESS_TOKEN_SECRET VARCHAR (512),
+ CONSUMER_KEY_ID INTEGER,
+ SCOPE VARCHAR(2048),
+ AUTHZ_USER VARCHAR (512),
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY (ACCESS_TOKEN),
+ FOREIGN KEY (CONSUMER_KEY_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_ACCESS_TOKEN (
+ TOKEN_ID VARCHAR (255),
+ ACCESS_TOKEN VARCHAR (2048),
+ REFRESH_TOKEN VARCHAR (2048),
+ CONSUMER_KEY_ID INTEGER,
+ AUTHZ_USER VARCHAR (100),
+ TENANT_ID INTEGER,
+ USER_DOMAIN VARCHAR(50),
+ USER_TYPE VARCHAR (25),
+ GRANT_TYPE VARCHAR (50),
+ TIME_CREATED TIMESTAMP DEFAULT 0,
+ REFRESH_TOKEN_TIME_CREATED TIMESTAMP DEFAULT 0,
+ VALIDITY_PERIOD BIGINT,
+ REFRESH_TOKEN_VALIDITY_PERIOD BIGINT,
+ TOKEN_SCOPE_HASH VARCHAR (32),
+ TOKEN_STATE VARCHAR (25) DEFAULT 'ACTIVE',
+ TOKEN_STATE_ID VARCHAR (128) DEFAULT 'NONE',
+ SUBJECT_IDENTIFIER VARCHAR(255),
+ ACCESS_TOKEN_HASH VARCHAR (512),
+ REFRESH_TOKEN_HASH VARCHAR (512),
+ IDP_ID INTEGER DEFAULT -1 NOT NULL,
+ TOKEN_BINDING_REF VARCHAR (32) DEFAULT 'NONE',
+ CONSENTED_TOKEN VARCHAR(6),
+ PRIMARY KEY (TOKEN_ID),
+ FOREIGN KEY (CONSUMER_KEY_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE,
+ CONSTRAINT CON_APP_KEY UNIQUE (CONSUMER_KEY_ID,AUTHZ_USER,TENANT_ID,USER_DOMAIN,USER_TYPE,TOKEN_SCOPE_HASH,
+ TOKEN_STATE,TOKEN_STATE_ID,IDP_ID,TOKEN_BINDING_REF)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_TOKEN_BINDING (
+ TOKEN_ID VARCHAR (255),
+ TOKEN_BINDING_TYPE VARCHAR (32),
+ TOKEN_BINDING_REF VARCHAR (32),
+ TOKEN_BINDING_VALUE VARCHAR (1024),
+ TENANT_ID INTEGER DEFAULT -1,
+ UNIQUE (TOKEN_ID,TOKEN_BINDING_TYPE,TOKEN_BINDING_VALUE),
+ FOREIGN KEY (TOKEN_ID) REFERENCES IDN_OAUTH2_ACCESS_TOKEN(TOKEN_ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_ACCESS_TOKEN_AUDIT (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ TOKEN_ID VARCHAR (255),
+ ACCESS_TOKEN VARCHAR(2048),
+ REFRESH_TOKEN VARCHAR(2048),
+ CONSUMER_KEY_ID INTEGER,
+ AUTHZ_USER VARCHAR (100),
+ TENANT_ID INTEGER,
+ USER_DOMAIN VARCHAR(50),
+ USER_TYPE VARCHAR (25),
+ GRANT_TYPE VARCHAR (50),
+ TIME_CREATED TIMESTAMP NULL,
+ REFRESH_TOKEN_TIME_CREATED TIMESTAMP NULL,
+ VALIDITY_PERIOD BIGINT,
+ REFRESH_TOKEN_VALIDITY_PERIOD BIGINT,
+ TOKEN_SCOPE_HASH VARCHAR(32),
+ TOKEN_STATE VARCHAR(25),
+ TOKEN_STATE_ID VARCHAR (128) ,
+ SUBJECT_IDENTIFIER VARCHAR(255),
+ ACCESS_TOKEN_HASH VARCHAR(512),
+ REFRESH_TOKEN_HASH VARCHAR(512),
+ INVALIDATED_TIME TIMESTAMP NULL,
+ IDP_ID INTEGER DEFAULT -1 NOT NULL,
+ PRIMARY KEY(ID)
+);
+
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_AUTHORIZATION_CODE (
+ CODE_ID VARCHAR (255),
+ AUTHORIZATION_CODE VARCHAR (2048),
+ CONSUMER_KEY_ID INTEGER,
+ CALLBACK_URL VARCHAR (2048),
+ SCOPE VARCHAR(2048),
+ AUTHZ_USER VARCHAR (100),
+ TENANT_ID INTEGER,
+ USER_DOMAIN VARCHAR(50),
+ TIME_CREATED TIMESTAMP,
+ VALIDITY_PERIOD BIGINT,
+ STATE VARCHAR (25) DEFAULT 'ACTIVE',
+ TOKEN_ID VARCHAR(255),
+ SUBJECT_IDENTIFIER VARCHAR(255),
+ PKCE_CODE_CHALLENGE VARCHAR (255),
+ PKCE_CODE_CHALLENGE_METHOD VARCHAR(128),
+ AUTHORIZATION_CODE_HASH VARCHAR (512),
+ IDP_ID INTEGER DEFAULT -1 NOT NULL,
+ PRIMARY KEY (CODE_ID),
+ FOREIGN KEY (CONSUMER_KEY_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_AUTHZ_CODE_SCOPE(
+ CODE_ID VARCHAR(255),
+ SCOPE VARCHAR(255),
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY (CODE_ID, SCOPE),
+ FOREIGN KEY (CODE_ID) REFERENCES IDN_OAUTH2_AUTHORIZATION_CODE (CODE_ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_DEVICE_FLOW (
+ CODE_ID VARCHAR(255),
+ DEVICE_CODE VARCHAR(255),
+ USER_CODE VARCHAR(25),
+ QUANTIFIER INTEGER NOT NULL DEFAULT 0,
+ CONSUMER_KEY_ID INTEGER,
+ LAST_POLL_TIME TIMESTAMP NOT NULL,
+ EXPIRY_TIME TIMESTAMP NOT NULL,
+ TIME_CREATED TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ POLL_TIME BIGINT,
+ STATUS VARCHAR (25) DEFAULT 'PENDING',
+ AUTHZ_USER VARCHAR (100),
+ TENANT_ID INTEGER,
+ USER_DOMAIN VARCHAR(50),
+ IDP_ID INTEGER,
+ PRIMARY KEY (DEVICE_CODE),
+ UNIQUE (CODE_ID),
+ CONSTRAINT USRCDE_QNTFR_CONSTRAINT UNIQUE (USER_CODE, QUANTIFIER),
+ FOREIGN KEY (CONSUMER_KEY_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_DEVICE_FLOW_SCOPES (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ SCOPE_ID VARCHAR(255),
+ SCOPE VARCHAR(255),
+ PRIMARY KEY (ID),
+ FOREIGN KEY (SCOPE_ID) REFERENCES IDN_OAUTH2_DEVICE_FLOW(CODE_ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_ACCESS_TOKEN_SCOPE (
+ TOKEN_ID VARCHAR (255),
+ TOKEN_SCOPE VARCHAR (255),
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY (TOKEN_ID, TOKEN_SCOPE),
+ FOREIGN KEY (TOKEN_ID) REFERENCES IDN_OAUTH2_ACCESS_TOKEN(TOKEN_ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_SCOPE (
+ SCOPE_ID INTEGER NOT NULL AUTO_INCREMENT,
+ NAME VARCHAR(255) NOT NULL,
+ DISPLAY_NAME VARCHAR(255) NOT NULL,
+ DESCRIPTION VARCHAR(512),
+ TENANT_ID INTEGER NOT NULL DEFAULT -1,
+ SCOPE_TYPE VARCHAR(255) NOT NULL,
+ PRIMARY KEY (SCOPE_ID),
+ UNIQUE (NAME, TENANT_ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_SCOPE_BINDING (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ SCOPE_ID INTEGER NOT NULL,
+ SCOPE_BINDING VARCHAR(255) NOT NULL,
+ BINDING_TYPE VARCHAR(255) NOT NULL,
+ FOREIGN KEY (SCOPE_ID) REFERENCES IDN_OAUTH2_SCOPE(SCOPE_ID) ON DELETE CASCADE,
+ UNIQUE (SCOPE_ID, SCOPE_BINDING, BINDING_TYPE),
+ PRIMARY KEY (ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_RESOURCE_SCOPE (
+ RESOURCE_PATH VARCHAR(255) NOT NULL,
+ SCOPE_ID INTEGER NOT NULL,
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY (RESOURCE_PATH),
+ FOREIGN KEY (SCOPE_ID) REFERENCES IDN_OAUTH2_SCOPE (SCOPE_ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_SCIM_GROUP (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ TENANT_ID INTEGER NOT NULL,
+ ROLE_NAME VARCHAR(255) NOT NULL,
+ ATTR_NAME VARCHAR(1024) NOT NULL,
+ ATTR_VALUE VARCHAR(1024),
+ UNIQUE(TENANT_ID, ROLE_NAME, ATTR_NAME),
+ PRIMARY KEY (ID)
+);
+
+
+
+CREATE TABLE IF NOT EXISTS IDN_OPENID_REMEMBER_ME (
+ USER_NAME VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER DEFAULT 0,
+ COOKIE_VALUE VARCHAR(1024),
+ CREATED_TIME TIMESTAMP,
+ PRIMARY KEY (USER_NAME, TENANT_ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OPENID_USER_RPS (
+ USER_NAME VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER DEFAULT 0,
+ RP_URL VARCHAR(255) NOT NULL,
+ TRUSTED_ALWAYS VARCHAR(128) DEFAULT 'FALSE',
+ LAST_VISIT DATE NOT NULL,
+ VISIT_COUNT INTEGER DEFAULT 0,
+ DEFAULT_PROFILE_NAME VARCHAR(255) DEFAULT 'DEFAULT',
+ PRIMARY KEY (USER_NAME, TENANT_ID, RP_URL)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OPENID_ASSOCIATIONS (
+ HANDLE VARCHAR(255) NOT NULL,
+ ASSOC_TYPE VARCHAR(255) NOT NULL,
+ EXPIRE_IN TIMESTAMP NOT NULL,
+ MAC_KEY VARCHAR(255) NOT NULL,
+ ASSOC_STORE VARCHAR(128) DEFAULT 'SHARED',
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY (HANDLE)
+);
+
+CREATE TABLE IDN_STS_STORE (
+ ID INTEGER AUTO_INCREMENT,
+ TOKEN_ID VARCHAR(255) NOT NULL,
+ TOKEN_CONTENT BLOB(1024) NOT NULL,
+ CREATE_DATE TIMESTAMP NOT NULL,
+ EXPIRE_DATE TIMESTAMP NOT NULL,
+ STATE INTEGER DEFAULT 0,
+ PRIMARY KEY (ID)
+);
+
+CREATE TABLE IDN_IDENTITY_USER_DATA (
+ TENANT_ID INTEGER DEFAULT -1234,
+ USER_NAME VARCHAR(255) NOT NULL,
+ DATA_KEY VARCHAR(255) NOT NULL,
+ DATA_VALUE VARCHAR(2048),
+ PRIMARY KEY (TENANT_ID, USER_NAME, DATA_KEY)
+);
+
+CREATE TABLE IDN_IDENTITY_META_DATA (
+ USER_NAME VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER DEFAULT -1234,
+ METADATA_TYPE VARCHAR(255) NOT NULL,
+ METADATA VARCHAR(255) NOT NULL,
+ VALID VARCHAR(255) NOT NULL,
+ PRIMARY KEY (TENANT_ID, USER_NAME, METADATA_TYPE,METADATA)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_THRIFT_SESSION (
+ SESSION_ID VARCHAR(255) NOT NULL,
+ USER_NAME VARCHAR(255) NOT NULL,
+ CREATED_TIME VARCHAR(255) NOT NULL,
+ LAST_MODIFIED_TIME VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY (SESSION_ID)
+);
+
+CREATE TABLE IDN_AUTH_SESSION_STORE (
+ SESSION_ID VARCHAR (100) NOT NULL,
+ SESSION_TYPE VARCHAR(100) NOT NULL,
+ OPERATION VARCHAR(10) NOT NULL,
+ SESSION_OBJECT BLOB,
+ TIME_CREATED BIGINT,
+ TENANT_ID INTEGER DEFAULT -1,
+ EXPIRY_TIME BIGINT,
+ PRIMARY KEY (SESSION_ID, SESSION_TYPE, TIME_CREATED, OPERATION)
+);
+
+
+CREATE TABLE IDN_AUTH_TEMP_SESSION_STORE (
+ SESSION_ID VARCHAR (100) NOT NULL,
+ SESSION_TYPE VARCHAR(100) NOT NULL,
+ OPERATION VARCHAR(10) NOT NULL,
+ SESSION_OBJECT BLOB,
+ TIME_CREATED BIGINT,
+ TENANT_ID INTEGER DEFAULT -1,
+ EXPIRY_TIME BIGINT,
+ PRIMARY KEY (SESSION_ID, SESSION_TYPE, TIME_CREATED, OPERATION)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_AUTH_USER (
+ USER_ID VARCHAR(255) NOT NULL,
+ USER_NAME VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ DOMAIN_NAME VARCHAR(255) NOT NULL,
+ IDP_ID INTEGER NOT NULL,
+ PRIMARY KEY (USER_ID),
+ CONSTRAINT USER_STORE_CONSTRAINT UNIQUE (USER_NAME, TENANT_ID, DOMAIN_NAME, IDP_ID));
+
+CREATE TABLE IF NOT EXISTS IDN_AUTH_USER_SESSION_MAPPING (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ USER_ID VARCHAR(255) NOT NULL,
+ SESSION_ID VARCHAR(255) NOT NULL,
+ CONSTRAINT USER_SESSION_STORE_CONSTRAINT UNIQUE (USER_ID, SESSION_ID),
+ PRIMARY KEY (ID));
+
+CREATE TABLE IF NOT EXISTS IDN_AUTH_SESSION_APP_INFO (
+ SESSION_ID VARCHAR (100) NOT NULL,
+ SUBJECT VARCHAR (100) NOT NULL,
+ APP_ID INTEGER NOT NULL,
+ INBOUND_AUTH_TYPE VARCHAR (255) NOT NULL,
+ PRIMARY KEY (SESSION_ID, SUBJECT, APP_ID, INBOUND_AUTH_TYPE));
+
+CREATE TABLE IF NOT EXISTS IDN_AUTH_SESSION_META_DATA (
+ SESSION_ID VARCHAR (100) NOT NULL,
+ PROPERTY_TYPE VARCHAR (100) NOT NULL,
+ `VALUE` VARCHAR (255) NOT NULL,
+ PRIMARY KEY (SESSION_ID, PROPERTY_TYPE, `VALUE`)
+ );
+
+CREATE TABLE IF NOT EXISTS SP_APP (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ TENANT_ID INTEGER NOT NULL,
+ APP_NAME VARCHAR (255) NOT NULL ,
+ USER_STORE VARCHAR (255) NOT NULL,
+ USERNAME VARCHAR (255) NOT NULL ,
+ DESCRIPTION VARCHAR (1024),
+ ROLE_CLAIM VARCHAR (512),
+ AUTH_TYPE VARCHAR (255) NOT NULL,
+ PROVISIONING_USERSTORE_DOMAIN VARCHAR (512),
+ IS_LOCAL_CLAIM_DIALECT CHAR(1) DEFAULT '1',
+ IS_SEND_LOCAL_SUBJECT_ID CHAR(1) DEFAULT '0',
+ IS_SEND_AUTH_LIST_OF_IDPS CHAR(1) DEFAULT '0',
+ IS_USE_TENANT_DOMAIN_SUBJECT CHAR(1) DEFAULT '1',
+ IS_USE_USER_DOMAIN_SUBJECT CHAR(1) DEFAULT '1',
+ ENABLE_AUTHORIZATION CHAR(1) DEFAULT '0',
+ SUBJECT_CLAIM_URI VARCHAR (512),
+ IS_SAAS_APP CHAR(1) DEFAULT '0',
+ IS_DUMB_MODE CHAR(1) DEFAULT '0',
+ UUID CHAR(36),
+ IMAGE_URL VARCHAR(1024),
+ ACCESS_URL VARCHAR(1024),
+ IS_DISCOVERABLE CHAR(1) DEFAULT '0',
+
+ PRIMARY KEY (ID));
+
+ALTER TABLE SP_APP ADD CONSTRAINT APPLICATION_NAME_CONSTRAINT UNIQUE(APP_NAME, TENANT_ID);
+ALTER TABLE SP_APP ADD CONSTRAINT APPLICATION_UUID_CONSTRAINT UNIQUE(UUID);
+
+CREATE TABLE IF NOT EXISTS SP_METADATA (
+ ID INTEGER AUTO_INCREMENT,
+ SP_ID INTEGER,
+ NAME VARCHAR(255) NOT NULL,
+ `VALUE` VARCHAR(255) NOT NULL,
+ DISPLAY_NAME VARCHAR(255),
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY (ID),
+ CONSTRAINT SP_METADATA_CONSTRAINT UNIQUE (SP_ID, NAME),
+ FOREIGN KEY (SP_ID) REFERENCES SP_APP(ID) ON DELETE CASCADE);
+
+CREATE TABLE IF NOT EXISTS SP_INBOUND_AUTH (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ TENANT_ID INTEGER NOT NULL,
+ INBOUND_AUTH_KEY VARCHAR (255),
+ INBOUND_AUTH_TYPE VARCHAR (255) NOT NULL,
+ INBOUND_CONFIG_TYPE VARCHAR (255) NOT NULL,
+ PROP_NAME VARCHAR (255),
+ PROP_VALUE VARCHAR (1024) ,
+ APP_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID));
+
+ALTER TABLE SP_INBOUND_AUTH ADD CONSTRAINT APPLICATION_ID_CONSTRAINT FOREIGN KEY (APP_ID) REFERENCES SP_APP (ID) ON DELETE CASCADE;
+
+CREATE TABLE IF NOT EXISTS SP_AUTH_STEP (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ TENANT_ID INTEGER NOT NULL,
+ STEP_ORDER INTEGER DEFAULT 1,
+ APP_ID INTEGER NOT NULL ,
+ IS_SUBJECT_STEP CHAR(1) DEFAULT '0',
+ IS_ATTRIBUTE_STEP CHAR(1) DEFAULT '0',
+ PRIMARY KEY (ID));
+
+ALTER TABLE SP_AUTH_STEP ADD CONSTRAINT APPLICATION_ID_CONSTRAINT_STEP FOREIGN KEY (APP_ID) REFERENCES SP_APP (ID) ON DELETE CASCADE;
+
+CREATE TABLE IF NOT EXISTS SP_FEDERATED_IDP (
+ ID INTEGER NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ AUTHENTICATOR_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID, AUTHENTICATOR_ID));
+
+ALTER TABLE SP_FEDERATED_IDP ADD CONSTRAINT STEP_ID_CONSTRAINT FOREIGN KEY (ID) REFERENCES SP_AUTH_STEP (ID) ON DELETE CASCADE;
+
+CREATE TABLE IF NOT EXISTS SP_CLAIM_DIALECT (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ TENANT_ID INTEGER NOT NULL,
+ SP_DIALECT VARCHAR (512) NOT NULL,
+ APP_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID));
+
+ALTER TABLE SP_CLAIM_DIALECT ADD CONSTRAINT DIALECTID_APPID_CONSTRAINT FOREIGN KEY (APP_ID) REFERENCES SP_APP (ID) ON DELETE CASCADE;
+
+CREATE TABLE IF NOT EXISTS SP_CLAIM_MAPPING (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ TENANT_ID INTEGER NOT NULL,
+ IDP_CLAIM VARCHAR (512) NOT NULL ,
+ SP_CLAIM VARCHAR (512) NOT NULL ,
+ APP_ID INTEGER NOT NULL,
+ IS_REQUESTED VARCHAR(128) DEFAULT '0',
+ IS_MANDATORY VARCHAR(128) DEFAULT '0',
+ DEFAULT_VALUE VARCHAR(255),
+ PRIMARY KEY (ID));
+
+ALTER TABLE SP_CLAIM_MAPPING ADD CONSTRAINT CLAIMID_APPID_CONSTRAINT FOREIGN KEY (APP_ID) REFERENCES SP_APP (ID) ON DELETE CASCADE;
+
+CREATE TABLE IF NOT EXISTS SP_ROLE_MAPPING (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ TENANT_ID INTEGER NOT NULL,
+ IDP_ROLE VARCHAR (255) NOT NULL ,
+ SP_ROLE VARCHAR (255) NOT NULL ,
+ APP_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID));
+
+ALTER TABLE SP_ROLE_MAPPING ADD CONSTRAINT ROLEID_APPID_CONSTRAINT FOREIGN KEY (APP_ID) REFERENCES SP_APP (ID) ON DELETE CASCADE;
+
+CREATE TABLE IF NOT EXISTS SP_REQ_PATH_AUTHENTICATOR (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ TENANT_ID INTEGER NOT NULL,
+ AUTHENTICATOR_NAME VARCHAR (255) NOT NULL ,
+ APP_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID));
+
+ALTER TABLE SP_REQ_PATH_AUTHENTICATOR ADD CONSTRAINT REQ_AUTH_APPID_CONSTRAINT FOREIGN KEY (APP_ID) REFERENCES SP_APP (ID) ON DELETE CASCADE;
+
+CREATE TABLE IF NOT EXISTS SP_PROVISIONING_CONNECTOR (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ TENANT_ID INTEGER NOT NULL,
+ IDP_NAME VARCHAR (255) NOT NULL ,
+ CONNECTOR_NAME VARCHAR (255) NOT NULL ,
+ APP_ID INTEGER NOT NULL,
+ IS_JIT_ENABLED CHAR(1) NOT NULL DEFAULT '0',
+ BLOCKING CHAR(1) NOT NULL DEFAULT '0',
+ RULE_ENABLED CHAR(1) NOT NULL DEFAULT '0',
+ PRIMARY KEY (ID));
+
+ALTER TABLE SP_PROVISIONING_CONNECTOR ADD CONSTRAINT PRO_CONNECTOR_APPID_CONSTRAINT FOREIGN KEY (APP_ID) REFERENCES SP_APP (ID) ON DELETE CASCADE;
+
+CREATE TABLE IF NOT EXISTS SP_AUTH_SCRIPT (
+ ID INTEGER AUTO_INCREMENT NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ APP_ID INTEGER NOT NULL,
+ TYPE VARCHAR(255) NOT NULL,
+ CONTENT BLOB DEFAULT NULL,
+ IS_ENABLED CHAR(1) NOT NULL DEFAULT '0',
+ PRIMARY KEY (ID));
+
+CREATE TABLE SP_TEMPLATE (
+ ID INTEGER AUTO_INCREMENT NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ NAME VARCHAR(255) NOT NULL,
+ DESCRIPTION VARCHAR(1023),
+ CONTENT BLOB DEFAULT NULL,
+ PRIMARY KEY (ID),
+ CONSTRAINT SP_TEMPLATE_CONSTRAINT UNIQUE (TENANT_ID, NAME));
+
+CREATE TABLE IF NOT EXISTS IDN_AUTH_WAIT_STATUS (
+ ID INTEGER AUTO_INCREMENT NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ LONG_WAIT_KEY VARCHAR(255) NOT NULL,
+ WAIT_STATUS CHAR(1) NOT NULL DEFAULT '1',
+ TIME_CREATED TIMESTAMP DEFAULT 0,
+ EXPIRE_TIME TIMESTAMP DEFAULT 0,
+ PRIMARY KEY (ID),
+ CONSTRAINT IDN_AUTH_WAIT_STATUS_KEY UNIQUE (LONG_WAIT_KEY));
+
+CREATE TABLE IF NOT EXISTS IDP (
+ ID INTEGER AUTO_INCREMENT,
+ TENANT_ID INTEGER,
+ NAME VARCHAR(254) NOT NULL,
+ IS_ENABLED CHAR(1) NOT NULL DEFAULT '1',
+ IS_PRIMARY CHAR(1) NOT NULL DEFAULT '0',
+ HOME_REALM_ID VARCHAR(254),
+ IMAGE MEDIUMBLOB,
+ CERTIFICATE BLOB,
+ ALIAS VARCHAR(254),
+ INBOUND_PROV_ENABLED CHAR(1) NOT NULL DEFAULT '0',
+ INBOUND_PROV_USER_STORE_ID VARCHAR(254),
+ USER_CLAIM_URI VARCHAR(254),
+ ROLE_CLAIM_URI VARCHAR(254),
+ DESCRIPTION VARCHAR(1024),
+ DEFAULT_AUTHENTICATOR_NAME VARCHAR(254),
+ DEFAULT_PRO_CONNECTOR_NAME VARCHAR(254),
+ PROVISIONING_ROLE VARCHAR(128),
+ IS_FEDERATION_HUB CHAR(1) NOT NULL DEFAULT '0',
+ IS_LOCAL_CLAIM_DIALECT CHAR(1) NOT NULL DEFAULT '0',
+ DISPLAY_NAME VARCHAR(255),
+ IMAGE_URL VARCHAR(1024),
+ UUID CHAR(36) NOT NULL,
+ PRIMARY KEY (ID),
+ UNIQUE (TENANT_ID, NAME),
+ UNIQUE (UUID)
+);
+
+CREATE TABLE IF NOT EXISTS IDP_ROLE (
+ ID INTEGER AUTO_INCREMENT,
+ IDP_ID INTEGER,
+ TENANT_ID INTEGER,
+ ROLE VARCHAR(254),
+ PRIMARY KEY (ID),
+ UNIQUE (IDP_ID, ROLE),
+ FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
+
+CREATE TABLE IF NOT EXISTS IDP_GROUP (
+ ID INTEGER AUTO_INCREMENT NOT NULL,
+ IDP_ID INTEGER NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ GROUP_NAME VARCHAR(255) NOT NULL,
+ UUID CHAR(36) NOT NULL,
+ PRIMARY KEY (ID),
+ UNIQUE (IDP_ID, GROUP_NAME),
+ UNIQUE (UUID),
+ FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
+
+CREATE TABLE IF NOT EXISTS IDP_ROLE_MAPPING (
+ ID INTEGER AUTO_INCREMENT,
+ IDP_ROLE_ID INTEGER,
+ TENANT_ID INTEGER,
+ USER_STORE_ID VARCHAR (253),
+ LOCAL_ROLE VARCHAR(253),
+ PRIMARY KEY (ID),
+ UNIQUE (IDP_ROLE_ID, TENANT_ID, USER_STORE_ID, LOCAL_ROLE),
+ FOREIGN KEY (IDP_ROLE_ID) REFERENCES IDP_ROLE(ID) ON DELETE CASCADE);
+
+CREATE TABLE IF NOT EXISTS IDP_CLAIM (
+ ID INTEGER AUTO_INCREMENT,
+ IDP_ID INTEGER,
+ TENANT_ID INTEGER,
+ CLAIM VARCHAR(254),
+ PRIMARY KEY (ID),
+ UNIQUE (IDP_ID, CLAIM),
+ FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
+
+CREATE TABLE IF NOT EXISTS IDP_CLAIM_MAPPING (
+ ID INTEGER AUTO_INCREMENT,
+ IDP_CLAIM_ID INTEGER,
+ TENANT_ID INTEGER,
+ LOCAL_CLAIM VARCHAR(253),
+ DEFAULT_VALUE VARCHAR(255),
+ IS_REQUESTED VARCHAR(128) DEFAULT '0',
+ PRIMARY KEY (ID),
+ UNIQUE (IDP_CLAIM_ID, TENANT_ID, LOCAL_CLAIM),
+ FOREIGN KEY (IDP_CLAIM_ID) REFERENCES IDP_CLAIM(ID) ON DELETE CASCADE);
+
+CREATE TABLE IF NOT EXISTS IDP_AUTHENTICATOR (
+ ID INTEGER AUTO_INCREMENT,
+ TENANT_ID INTEGER,
+ IDP_ID INTEGER,
+ NAME VARCHAR(255) NOT NULL,
+ IS_ENABLED CHAR (1) DEFAULT '1',
+ DISPLAY_NAME VARCHAR(255),
+ PRIMARY KEY (ID),
+ UNIQUE (TENANT_ID, IDP_ID, NAME),
+ FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
+
+CREATE TABLE IF NOT EXISTS IDP_METADATA (
+ ID INTEGER AUTO_INCREMENT,
+ IDP_ID INTEGER,
+ NAME VARCHAR(255) NOT NULL,
+ `VALUE` VARCHAR(255) NOT NULL,
+ DISPLAY_NAME VARCHAR(255),
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY (ID),
+ CONSTRAINT IDP_METADATA_CONSTRAINT UNIQUE (IDP_ID, NAME),
+ FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
+
+CREATE TABLE IF NOT EXISTS IDP_AUTHENTICATOR_PROPERTY (
+ ID INTEGER AUTO_INCREMENT,
+ TENANT_ID INTEGER,
+ AUTHENTICATOR_ID INTEGER,
+ PROPERTY_KEY VARCHAR(255) NOT NULL,
+ PROPERTY_VALUE VARCHAR(2047),
+ IS_SECRET CHAR (1) DEFAULT '0',
+ PRIMARY KEY (ID),
+ UNIQUE (TENANT_ID, AUTHENTICATOR_ID, PROPERTY_KEY),
+ FOREIGN KEY (AUTHENTICATOR_ID) REFERENCES IDP_AUTHENTICATOR(ID) ON DELETE CASCADE);
+
+CREATE TABLE IF NOT EXISTS IDP_PROVISIONING_CONFIG (
+ ID INTEGER AUTO_INCREMENT,
+ TENANT_ID INTEGER,
+ IDP_ID INTEGER,
+ PROVISIONING_CONNECTOR_TYPE VARCHAR(255) NOT NULL,
+ IS_ENABLED CHAR (1) DEFAULT '0',
+ IS_BLOCKING CHAR (1) DEFAULT '0',
+ IS_RULES_ENABLED CHAR (1) DEFAULT '0',
+ PRIMARY KEY (ID),
+ UNIQUE (TENANT_ID, IDP_ID, PROVISIONING_CONNECTOR_TYPE),
+ FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
+
+CREATE TABLE IF NOT EXISTS IDP_PROV_CONFIG_PROPERTY (
+ ID INTEGER AUTO_INCREMENT,
+ TENANT_ID INTEGER,
+ PROVISIONING_CONFIG_ID INTEGER,
+ PROPERTY_KEY VARCHAR(255) NOT NULL,
+ PROPERTY_VALUE VARCHAR(2048),
+ PROPERTY_BLOB_VALUE BLOB,
+ PROPERTY_TYPE VARCHAR(32) NOT NULL,
+ IS_SECRET CHAR (1) DEFAULT '0',
+ PRIMARY KEY (ID),
+ UNIQUE (TENANT_ID, PROVISIONING_CONFIG_ID, PROPERTY_KEY),
+ FOREIGN KEY (PROVISIONING_CONFIG_ID) REFERENCES IDP_PROVISIONING_CONFIG(ID) ON DELETE CASCADE);
+
+CREATE TABLE IF NOT EXISTS IDP_PROVISIONING_ENTITY (
+ ID INTEGER AUTO_INCREMENT,
+ PROVISIONING_CONFIG_ID INTEGER,
+ ENTITY_TYPE VARCHAR(255) NOT NULL,
+ ENTITY_LOCAL_USERSTORE VARCHAR(255) NOT NULL,
+ ENTITY_NAME VARCHAR(255) NOT NULL,
+ ENTITY_VALUE VARCHAR(255),
+ TENANT_ID INTEGER,
+ ENTITY_LOCAL_ID VARCHAR(255),
+ PRIMARY KEY (ID),
+ UNIQUE (ENTITY_TYPE, TENANT_ID, ENTITY_LOCAL_USERSTORE, ENTITY_NAME, PROVISIONING_CONFIG_ID),
+ UNIQUE (PROVISIONING_CONFIG_ID, ENTITY_TYPE, ENTITY_VALUE),
+ FOREIGN KEY (PROVISIONING_CONFIG_ID) REFERENCES IDP_PROVISIONING_CONFIG(ID) ON DELETE CASCADE);
+
+CREATE TABLE IF NOT EXISTS IDP_LOCAL_CLAIM (
+ ID INTEGER AUTO_INCREMENT,
+ TENANT_ID INTEGER,
+ IDP_ID INTEGER,
+ CLAIM_URI VARCHAR(255) NOT NULL,
+ DEFAULT_VALUE VARCHAR(255),
+ IS_REQUESTED VARCHAR(128) DEFAULT '0',
+ PRIMARY KEY (ID),
+ UNIQUE (TENANT_ID, IDP_ID, CLAIM_URI),
+ FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
+
+CREATE TABLE IF NOT EXISTS IDN_ASSOCIATED_ID (
+ ID INTEGER AUTO_INCREMENT,
+ IDP_USER_ID VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER DEFAULT -1234,
+ IDP_ID INTEGER NOT NULL,
+ DOMAIN_NAME VARCHAR(255) NOT NULL,
+ USER_NAME VARCHAR(255) NOT NULL,
+ ASSOCIATION_ID CHAR(36) NOT NULL,
+ PRIMARY KEY (ID),
+ UNIQUE(IDP_USER_ID, TENANT_ID, IDP_ID),
+ FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_USER_ACCOUNT_ASSOCIATION (
+ ASSOCIATION_KEY VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER,
+ DOMAIN_NAME VARCHAR(255) NOT NULL,
+ USER_NAME VARCHAR(255) NOT NULL,
+ PRIMARY KEY (TENANT_ID, DOMAIN_NAME, USER_NAME));
+
+CREATE TABLE IF NOT EXISTS FIDO_DEVICE_STORE (
+ TENANT_ID INTEGER,
+ DOMAIN_NAME VARCHAR(255) NOT NULL,
+ USER_NAME VARCHAR(45) NOT NULL,
+ TIME_REGISTERED TIMESTAMP,
+ KEY_HANDLE VARCHAR(200) NOT NULL,
+ DEVICE_DATA VARCHAR(2048) NOT NULL,
+ PRIMARY KEY (TENANT_ID, DOMAIN_NAME, USER_NAME, KEY_HANDLE));
+
+CREATE TABLE IF NOT EXISTS FIDO2_DEVICE_STORE (
+ TENANT_ID INTEGER,
+ DOMAIN_NAME VARCHAR(255) NOT NULL,
+ USER_NAME VARCHAR(45) NOT NULL,
+ TIME_REGISTERED TIMESTAMP,
+ USER_HANDLE VARCHAR(200) NOT NULL,
+ CREDENTIAL_ID VARCHAR(200) NOT NULL,
+ PUBLIC_KEY_COSE VARCHAR(2048) NOT NULL,
+ SIGNATURE_COUNT BIGINT,
+ USER_IDENTITY VARCHAR(200) NOT NULL,
+ DISPLAY_NAME VARCHAR(255),
+ IS_USERNAMELESS_SUPPORTED CHAR(1) DEFAULT '0',
+ PRIMARY KEY (TENANT_ID, DOMAIN_NAME, USER_NAME, USER_HANDLE));
+
+CREATE TABLE IF NOT EXISTS WF_REQUEST (
+ UUID VARCHAR (45),
+ CREATED_BY VARCHAR (255),
+ TENANT_ID INTEGER DEFAULT -1,
+ OPERATION_TYPE VARCHAR (50),
+ CREATED_AT TIMESTAMP,
+ UPDATED_AT TIMESTAMP,
+ STATUS VARCHAR (30),
+ REQUEST BLOB,
+ PRIMARY KEY (UUID)
+);
+
+CREATE TABLE IF NOT EXISTS WF_BPS_PROFILE (
+ PROFILE_NAME VARCHAR(45),
+ HOST_URL_MANAGER VARCHAR(255),
+ HOST_URL_WORKER VARCHAR(255),
+ USERNAME VARCHAR(100),
+ PASSWORD VARCHAR(1023),
+ CALLBACK_HOST VARCHAR (45),
+ CALLBACK_USERNAME VARCHAR(100),
+ CALLBACK_PASSWORD VARCHAR(255),
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY (PROFILE_NAME, TENANT_ID)
+);
+
+CREATE TABLE IF NOT EXISTS WF_WORKFLOW(
+ ID VARCHAR (45),
+ WF_NAME VARCHAR (45),
+ DESCRIPTION VARCHAR (255),
+ TEMPLATE_ID VARCHAR (45),
+ IMPL_ID VARCHAR (45),
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY (ID)
+);
+
+CREATE TABLE IF NOT EXISTS WF_WORKFLOW_ASSOCIATION(
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ ASSOC_NAME VARCHAR (45),
+ EVENT_ID VARCHAR(45),
+ ASSOC_CONDITION VARCHAR (2000),
+ WORKFLOW_ID VARCHAR (45),
+ IS_ENABLED CHAR (1) DEFAULT '1',
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY(ID),
+ FOREIGN KEY (WORKFLOW_ID) REFERENCES WF_WORKFLOW(ID)ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS WF_WORKFLOW_CONFIG_PARAM(
+ WORKFLOW_ID VARCHAR (45),
+ PARAM_NAME VARCHAR (45),
+ PARAM_VALUE VARCHAR (1000),
+ PARAM_QNAME VARCHAR (45),
+ PARAM_HOLDER VARCHAR (45),
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY (WORKFLOW_ID, PARAM_NAME, PARAM_QNAME, PARAM_HOLDER),
+ FOREIGN KEY (WORKFLOW_ID) REFERENCES WF_WORKFLOW(ID)ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS WF_REQUEST_ENTITY_RELATIONSHIP(
+ REQUEST_ID VARCHAR (45),
+ ENTITY_NAME VARCHAR (255),
+ ENTITY_TYPE VARCHAR (50),
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY(REQUEST_ID, ENTITY_NAME, ENTITY_TYPE, TENANT_ID),
+ FOREIGN KEY (REQUEST_ID) REFERENCES WF_REQUEST(UUID)ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS WF_WORKFLOW_REQUEST_RELATION(
+ RELATIONSHIP_ID VARCHAR (45),
+ WORKFLOW_ID VARCHAR (45),
+ REQUEST_ID VARCHAR (45),
+ UPDATED_AT TIMESTAMP,
+ STATUS VARCHAR (30),
+ TENANT_ID INTEGER DEFAULT -1,
+ PRIMARY KEY (RELATIONSHIP_ID),
+ FOREIGN KEY (WORKFLOW_ID) REFERENCES WF_WORKFLOW(ID)ON DELETE CASCADE,
+ FOREIGN KEY (REQUEST_ID) REFERENCES WF_REQUEST(UUID)ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_RECOVERY_DATA (
+ USER_NAME VARCHAR(255) NOT NULL,
+ USER_DOMAIN VARCHAR(127) NOT NULL,
+ TENANT_ID INTEGER DEFAULT -1,
+ CODE VARCHAR(255) NOT NULL,
+ SCENARIO VARCHAR(255) NOT NULL,
+ STEP VARCHAR(127) NOT NULL,
+ TIME_CREATED TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ REMAINING_SETS VARCHAR(2500) DEFAULT NULL,
+ PRIMARY KEY(USER_NAME, USER_DOMAIN, TENANT_ID, SCENARIO,STEP),
+ UNIQUE(CODE)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_PASSWORD_HISTORY_DATA (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ USER_NAME VARCHAR(255) NOT NULL,
+ USER_DOMAIN VARCHAR(127) NOT NULL,
+ TENANT_ID INTEGER DEFAULT -1,
+ SALT_VALUE VARCHAR(255),
+ HASH VARCHAR(255) NOT NULL,
+ TIME_CREATED TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (ID),
+ UNIQUE (USER_NAME,USER_DOMAIN,TENANT_ID,SALT_VALUE,HASH)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_CLAIM_DIALECT (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ DIALECT_URI VARCHAR (255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ CONSTRAINT DIALECT_URI_CONSTRAINT UNIQUE (DIALECT_URI, TENANT_ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_CLAIM (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ DIALECT_ID INTEGER NOT NULL,
+ CLAIM_URI VARCHAR (255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (DIALECT_ID) REFERENCES IDN_CLAIM_DIALECT(ID) ON DELETE CASCADE,
+ CONSTRAINT CLAIM_URI_CONSTRAINT UNIQUE (DIALECT_ID, CLAIM_URI, TENANT_ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_CLAIM_MAPPED_ATTRIBUTE (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ LOCAL_CLAIM_ID INTEGER,
+ USER_STORE_DOMAIN_NAME VARCHAR (255) NOT NULL,
+ ATTRIBUTE_NAME VARCHAR (255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (LOCAL_CLAIM_ID) REFERENCES IDN_CLAIM(ID) ON DELETE CASCADE,
+ CONSTRAINT USER_STORE_DOMAIN_CONSTRAINT UNIQUE (LOCAL_CLAIM_ID, USER_STORE_DOMAIN_NAME, TENANT_ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_CLAIM_PROPERTY (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ LOCAL_CLAIM_ID INTEGER,
+ PROPERTY_NAME VARCHAR (255) NOT NULL,
+ PROPERTY_VALUE VARCHAR (255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (LOCAL_CLAIM_ID) REFERENCES IDN_CLAIM(ID) ON DELETE CASCADE,
+ CONSTRAINT PROPERTY_NAME_CONSTRAINT UNIQUE (LOCAL_CLAIM_ID, PROPERTY_NAME, TENANT_ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_CLAIM_MAPPING (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ EXT_CLAIM_ID INTEGER NOT NULL,
+ MAPPED_LOCAL_CLAIM_ID INTEGER NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (EXT_CLAIM_ID) REFERENCES IDN_CLAIM(ID) ON DELETE CASCADE,
+ FOREIGN KEY (MAPPED_LOCAL_CLAIM_ID) REFERENCES IDN_CLAIM(ID) ON DELETE CASCADE,
+ CONSTRAINT EXT_TO_LOC_MAPPING_CONSTRN UNIQUE (EXT_CLAIM_ID, TENANT_ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_SAML2_ASSERTION_STORE (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ SAML2_ID VARCHAR(255) ,
+ SAML2_ISSUER VARCHAR(255) ,
+ SAML2_SUBJECT VARCHAR(255) ,
+ SAML2_SESSION_INDEX VARCHAR(255) ,
+ SAML2_AUTHN_CONTEXT_CLASS_REF VARCHAR(255) ,
+ SAML2_ASSERTION VARCHAR(4096) ,
+ ASSERTION BLOB ,
+ PRIMARY KEY (ID)
+);
+
+CREATE TABLE IDN_SAML2_ARTIFACT_STORE (
+ ID INT NOT NULL AUTO_INCREMENT,
+ SOURCE_ID VARCHAR(255) NOT NULL,
+ MESSAGE_HANDLER VARCHAR(255) NOT NULL,
+ AUTHN_REQ_DTO BLOB NOT NULL,
+ SESSION_ID VARCHAR(255) NOT NULL,
+ INIT_TIMESTAMP TIMESTAMP NOT NULL,
+ EXP_TIMESTAMP TIMESTAMP NOT NULL,
+ ASSERTION_ID VARCHAR(255),
+ PRIMARY KEY (`ID`)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OIDC_JTI (
+ JWT_ID VARCHAR(255),
+ TENANT_ID INTEGER NOT NULL,
+ EXP_TIME TIMESTAMP NOT NULL ,
+ TIME_CREATED TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
+ PRIMARY KEY (JWT_ID, TENANT_ID)
+);
+
+
+CREATE TABLE IF NOT EXISTS IDN_OIDC_PROPERTY (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ TENANT_ID INTEGER,
+ CONSUMER_KEY VARCHAR(255) ,
+ PROPERTY_KEY VARCHAR(255) NOT NULL,
+ PROPERTY_VALUE VARCHAR(2047) ,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (CONSUMER_KEY) REFERENCES IDN_OAUTH_CONSUMER_APPS(CONSUMER_KEY) ON DELETE CASCADE
+);
+CREATE TABLE IF NOT EXISTS IDN_OIDC_REQ_OBJECT_REFERENCE (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ CONSUMER_KEY_ID INTEGER ,
+ CODE_ID VARCHAR(255) ,
+ TOKEN_ID VARCHAR(255) ,
+ SESSION_DATA_KEY VARCHAR(255),
+ PRIMARY KEY (ID),
+ FOREIGN KEY (CONSUMER_KEY_ID) REFERENCES IDN_OAUTH_CONSUMER_APPS(ID) ON DELETE CASCADE,
+ FOREIGN KEY (TOKEN_ID) REFERENCES IDN_OAUTH2_ACCESS_TOKEN(TOKEN_ID) ON DELETE CASCADE,
+ FOREIGN KEY (CODE_ID) REFERENCES IDN_OAUTH2_AUTHORIZATION_CODE(CODE_ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OIDC_REQ_OBJECT_CLAIMS (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ REQ_OBJECT_ID INTEGER,
+ CLAIM_ATTRIBUTE VARCHAR(255) ,
+ ESSENTIAL CHAR(1) NOT NULL DEFAULT '0',
+ `VALUE` VARCHAR(255) ,
+ IS_USERINFO CHAR(1) NOT NULL DEFAULT '0',
+ PRIMARY KEY (ID),
+ FOREIGN KEY (REQ_OBJECT_ID) REFERENCES IDN_OIDC_REQ_OBJECT_REFERENCE (ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OIDC_REQ_OBJ_CLAIM_VALUES (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ REQ_OBJECT_CLAIMS_ID INTEGER ,
+ CLAIM_VALUES VARCHAR(255) ,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (REQ_OBJECT_CLAIMS_ID) REFERENCES IDN_OIDC_REQ_OBJECT_CLAIMS(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_CERTIFICATE (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ NAME VARCHAR(100),
+ CERTIFICATE_IN_PEM BLOB,
+ TENANT_ID INTEGER DEFAULT 0,
+ PRIMARY KEY(ID),
+ CONSTRAINT CERTIFICATE_UNIQUE_KEY UNIQUE (NAME, TENANT_ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OIDC_SCOPE_CLAIM_MAPPING (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ SCOPE_ID INTEGER NOT NULL,
+ EXTERNAL_CLAIM_ID INTEGER NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (SCOPE_ID) REFERENCES IDN_OAUTH2_SCOPE(SCOPE_ID) ON DELETE CASCADE,
+ FOREIGN KEY (EXTERNAL_CLAIM_ID) REFERENCES IDN_CLAIM(ID) ON DELETE CASCADE,
+ UNIQUE (SCOPE_ID, EXTERNAL_CLAIM_ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_FUNCTION_LIBRARY (
+ NAME VARCHAR(255) NOT NULL,
+ DESCRIPTION VARCHAR(1023),
+ TYPE VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ DATA BLOB NOT NULL,
+ PRIMARY KEY (TENANT_ID,NAME)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_CIBA_AUTH_CODE (
+ AUTH_CODE_KEY CHAR (36),
+ AUTH_REQ_ID CHAR (36),
+ ISSUED_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ CONSUMER_KEY VARCHAR(255),
+ LAST_POLLED_TIME TIMESTAMP NOT NULL,
+ POLLING_INTERVAL INTEGER,
+ EXPIRES_IN INTEGER,
+ AUTHENTICATED_USER_NAME VARCHAR(255),
+ USER_STORE_DOMAIN VARCHAR(100),
+ TENANT_ID INTEGER,
+ AUTH_REQ_STATUS VARCHAR (100) DEFAULT 'REQUESTED',
+ IDP_ID INTEGER,
+ UNIQUE(AUTH_REQ_ID),
+ PRIMARY KEY (AUTH_CODE_KEY),
+ FOREIGN KEY (CONSUMER_KEY) REFERENCES IDN_OAUTH_CONSUMER_APPS(CONSUMER_KEY) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_CIBA_REQUEST_SCOPES (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ AUTH_CODE_KEY CHAR (36),
+ SCOPE VARCHAR (255),
+ FOREIGN KEY (AUTH_CODE_KEY) REFERENCES IDN_OAUTH2_CIBA_AUTH_CODE(AUTH_CODE_KEY) ON DELETE CASCADE,
+ PRIMARY KEY (ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_FED_AUTH_SESSION_MAPPING (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ IDP_SESSION_ID VARCHAR(255) NOT NULL,
+ SESSION_ID VARCHAR(255) NOT NULL,
+ IDP_NAME VARCHAR(255) NOT NULL,
+ AUTHENTICATOR_ID VARCHAR(255),
+ PROTOCOL_TYPE VARCHAR(255),
+ TIME_CREATED TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ TENANT_ID INTEGER NOT NULL DEFAULT 0,
+ IDP_ID INTEGER NOT NULL DEFAULT 0,
+ FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE,
+ PRIMARY KEY (ID),
+ UNIQUE (IDP_SESSION_ID, TENANT_ID, IDP_ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_CONFIG_TYPE (
+ ID VARCHAR(255) NOT NULL,
+ NAME VARCHAR(255) NOT NULL,
+ DESCRIPTION VARCHAR(1023) NULL,
+ PRIMARY KEY (ID),
+ CONSTRAINT TYPE_NAME_CONSTRAINT UNIQUE (NAME)
+);
+
+INSERT INTO IDN_CONFIG_TYPE (ID, NAME, DESCRIPTION) VALUES
+('9ab0ef95-13e9-4ed5-afaf-d29bed62f7bd', 'IDP_TEMPLATE', 'Template type to uniquely identify IDP templates'),
+('3c4ac3d0-5903-4e3d-aaca-38df65b33bfd', 'APPLICATION_TEMPLATE', 'Template type to uniquely identify Application templates'),
+('8ec6dbf1-218a-49bf-bc34-0d2db52d151c', 'CORS_CONFIGURATION', 'A resource type to keep the tenant CORS configurations'),
+('669b99ca-cdb0-44a6-8cae-babed3b585df', 'Publisher', 'A resource type to keep the event publisher configurations'),
+('73f6d9ca-62f4-4566-bab9-2a930ae51ba8', 'BRANDING_PREFERENCES', 'A resource type to keep the tenant branding preferences'),
+('899c69b2-8bf7-46b5-9666-f7f99f90d6cc', 'fido-config', 'A resource type to store FIDO authenticator related preferences'),
+('7f24050f-3e3d-4a00-b10f-fd5450d6523e', 'input-validation-configurations', 'A resource type to store input validation related configurations'),
+('f4e83b8a-d1c4-a0d6-03a7-d48e268c60c5', 'PK_JWT_CONFIGURATION', 'A resource type to keep the tenant private key jwt configuration.'),
+('9ec61e9d-f0e6-4952-9a09-ab842aeb2db2', 'ATTRIBUTE_CONFIGURATION', 'A resource type to store attribute related configurations.');
+
+CREATE TABLE IF NOT EXISTS IDN_CONFIG_RESOURCE (
+ ID VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ NAME VARCHAR(255) NOT NULL,
+ CREATED_TIME TIMESTAMP NOT NULL,
+ LAST_MODIFIED TIMESTAMP NOT NULL,
+ HAS_FILE BOOLEAN NOT NULL,
+ HAS_ATTRIBUTE BOOLEAN NOT NULL,
+ TYPE_ID VARCHAR(255) NOT NULL,
+ UNIQUE (NAME, TENANT_ID, TYPE_ID),
+ PRIMARY KEY (ID)
+);
+ALTER TABLE IDN_CONFIG_RESOURCE
+ADD CONSTRAINT TYPE_ID_FOREIGN_CONSTRAINT FOREIGN KEY (TYPE_ID) REFERENCES IDN_CONFIG_TYPE (ID)
+ON DELETE CASCADE ON UPDATE CASCADE;
+
+CREATE TABLE IF NOT EXISTS IDN_CONFIG_ATTRIBUTE (
+ ID VARCHAR(255) NOT NULL,
+ RESOURCE_ID VARCHAR(255) NOT NULL,
+ ATTR_KEY VARCHAR(255) NOT NULL,
+ ATTR_VALUE VARCHAR(1023) NULL,
+ PRIMARY KEY (ID),
+ UNIQUE (RESOURCE_ID, ATTR_KEY)
+);
+ALTER TABLE IDN_CONFIG_ATTRIBUTE
+ADD CONSTRAINT RESOURCE_ID_ATTRIBUTE_FOREIGN_CONSTRAINT FOREIGN KEY (RESOURCE_ID) REFERENCES
+IDN_CONFIG_RESOURCE (ID) ON DELETE CASCADE ON UPDATE CASCADE;
+
+CREATE TABLE IF NOT EXISTS IDN_CONFIG_FILE (
+ ID VARCHAR(255) NOT NULL,
+ `VALUE` BLOB NULL,
+ RESOURCE_ID VARCHAR(255) NOT NULL,
+ NAME VARCHAR(255) NULL,
+ PRIMARY KEY (ID)
+);
+ALTER TABLE IDN_CONFIG_FILE
+ADD CONSTRAINT RESOURCE_ID_FILE_FOREIGN_CONSTRAINT FOREIGN KEY (RESOURCE_ID) REFERENCES
+IDN_CONFIG_RESOURCE (ID) ON DELETE CASCADE ON UPDATE CASCADE;
+
+CREATE TABLE IF NOT EXISTS IDN_REMOTE_FETCH_CONFIG (
+ ID VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ IS_ENABLED CHAR(1) NOT NULL,
+ REPO_MANAGER_TYPE VARCHAR(255) NOT NULL,
+ ACTION_LISTENER_TYPE VARCHAR(255) NOT NULL,
+ CONFIG_DEPLOYER_TYPE VARCHAR(255) NOT NULL,
+ REMOTE_FETCH_NAME VARCHAR(255),
+ REMOTE_RESOURCE_URI VARCHAR(255) NOT NULL,
+ ATTRIBUTES_JSON MEDIUMTEXT NOT NULL,
+ PRIMARY KEY (ID),
+ CONSTRAINT UC_REMOTE_RESOURCE_TYPE UNIQUE (TENANT_ID, CONFIG_DEPLOYER_TYPE)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_REMOTE_FETCH_REVISIONS (
+ ID VARCHAR(255) NOT NULL,
+ CONFIG_ID VARCHAR(255) NOT NULL,
+ FILE_PATH VARCHAR(255) NOT NULL,
+ FILE_HASH VARCHAR(255),
+ DEPLOYED_DATE TIMESTAMP,
+ LAST_SYNC_TIME TIMESTAMP,
+ DEPLOYMENT_STATUS VARCHAR(255),
+ ITEM_NAME VARCHAR(255),
+ DEPLOY_ERR_LOG MEDIUMTEXT,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (CONFIG_ID) REFERENCES IDN_REMOTE_FETCH_CONFIG(ID) ON DELETE CASCADE,
+ CONSTRAINT UC_REVISIONS UNIQUE (CONFIG_ID, ITEM_NAME)
+);
+
+
+CREATE TABLE IF NOT EXISTS IDN_USER_FUNCTIONALITY_MAPPING (
+ ID VARCHAR(255) NOT NULL,
+ USER_ID VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ FUNCTIONALITY_ID VARCHAR(255) NOT NULL,
+ IS_FUNCTIONALITY_LOCKED BOOLEAN NOT NULL,
+ FUNCTIONALITY_UNLOCK_TIME BIGINT NOT NULL,
+ FUNCTIONALITY_LOCK_REASON VARCHAR(1023),
+ FUNCTIONALITY_LOCK_REASON_CODE VARCHAR(255),
+ PRIMARY KEY (ID),
+ CONSTRAINT IDN_USER_FUNCTIONALITY_MAPPING_CONSTRAINT UNIQUE (USER_ID, TENANT_ID, FUNCTIONALITY_ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_USER_FUNCTIONALITY_PROPERTY (
+ ID VARCHAR(255) NOT NULL,
+ USER_ID VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ FUNCTIONALITY_ID VARCHAR(255) NOT NULL,
+ PROPERTY_NAME VARCHAR(255),
+ PROPERTY_VALUE VARCHAR(255),
+ PRIMARY KEY (ID),
+ CONSTRAINT IDN_USER_FUNCTIONALITY_PROPERTY_CONSTRAINT UNIQUE (USER_ID, TENANT_ID, FUNCTIONALITY_ID, PROPERTY_NAME)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_CORS_ORIGIN (
+ ID INT NOT NULL AUTO_INCREMENT,
+ TENANT_ID INT NOT NULL,
+ ORIGIN VARCHAR(2048) NOT NULL,
+ UUID CHAR(36) NOT NULL,
+
+ PRIMARY KEY (ID),
+ UNIQUE (TENANT_ID, ORIGIN),
+ UNIQUE (UUID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_CORS_ASSOCIATION (
+ IDN_CORS_ORIGIN_ID INT NOT NULL,
+ SP_APP_ID INT NOT NULL,
+
+ PRIMARY KEY (IDN_CORS_ORIGIN_ID, SP_APP_ID),
+ FOREIGN KEY (IDN_CORS_ORIGIN_ID) REFERENCES IDN_CORS_ORIGIN (ID) ON DELETE CASCADE,
+ FOREIGN KEY (SP_APP_ID) REFERENCES SP_APP (ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_USER_CONSENT (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ USER_ID VARCHAR(255) NOT NULL,
+ APP_ID CHAR(36) NOT NULL,
+ TENANT_ID INTEGER NOT NULL DEFAULT -1,
+ CONSENT_ID VARCHAR(255) NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE,
+ UNIQUE (USER_ID, APP_ID, TENANT_ID),
+ UNIQUE (CONSENT_ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH2_USER_CONSENTED_SCOPES (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ CONSENT_ID VARCHAR(255) NOT NULL,
+ TENANT_ID INTEGER NOT NULL DEFAULT -1,
+ SCOPE VARCHAR(255) NOT NULL,
+ CONSENT BOOLEAN NOT NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (CONSENT_ID) REFERENCES IDN_OAUTH2_USER_CONSENT(CONSENT_ID) ON DELETE CASCADE,
+ UNIQUE (CONSENT_ID, SCOPE)
+);
+
+CREATE TABLE IF NOT EXISTS IDN_SECRET_TYPE (
+ ID VARCHAR(255) NOT NULL,
+ NAME VARCHAR(255) NOT NULL,
+ DESCRIPTION VARCHAR(1023) NULL,
+ PRIMARY KEY (ID),
+ CONSTRAINT SECRET_TYPE_NAME_CONSTRAINT UNIQUE (NAME)
+);
+
+INSERT INTO IDN_SECRET_TYPE (ID, NAME, DESCRIPTION) VALUES
+('1358bdbf-e0cc-4268-a42c-c3e0960e13f0', 'ADAPTIVE_AUTH_CALL_CHOREO', 'Secret type to uniquely identify secrets relevant to callChoreo adaptive auth function'),
+('c508ca28-60c0-4493-a758-77e4173ffdb9', 'IDP_SECRET_PROPERTIES', 'Secret type to uniquely identify secrets relevant to identity providers'),
+('433df096-62b7-4a36-b3eb-1bed9150ed35', 'IDVP_SECRET_PROPERTIES', 'Secret type to uniquely identify secrets relevant to identity verification providers');
+
+CREATE TABLE IF NOT EXISTS IDN_SECRET (
+ ID VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ SECRET_NAME VARCHAR(1023) NOT NULL,
+ SECRET_VALUE VARCHAR(8000) NOT NULL,
+ CREATED_TIME TIMESTAMP NOT NULL,
+ LAST_MODIFIED TIMESTAMP NOT NULL,
+ TYPE_ID VARCHAR(255) NOT NULL,
+ DESCRIPTION VARCHAR(1023) NULL,
+ KEY_ID VARCHAR(255) NULL,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (TYPE_ID) REFERENCES IDN_SECRET_TYPE(ID) ON DELETE CASCADE,
+ UNIQUE (SECRET_NAME, TENANT_ID, TYPE_ID)
+);
+
+CREATE TABLE IF NOT EXISTS SP_SHARED_APP (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ MAIN_APP_ID CHAR(36) NOT NULL,
+ OWNER_ORG_ID CHAR(36) NOT NULL,
+ SHARED_APP_ID CHAR(36) NOT NULL,
+ SHARED_ORG_ID CHAR(36) NOT NULL,
+ SHARE_WITH_ALL_CHILDREN BOOLEAN DEFAULT FALSE,
+ PRIMARY KEY (ID),
+ FOREIGN KEY (MAIN_APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE,
+ FOREIGN KEY (SHARED_APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE,
+ UNIQUE (MAIN_APP_ID, OWNER_ORG_ID, SHARED_ORG_ID),
+ UNIQUE (SHARED_APP_ID)
+);
+
+CREATE TABLE IF NOT EXISTS IDVP (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ UUID CHAR(36) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ IDVP_TYPE VARCHAR(254),
+ NAME VARCHAR(254),
+ DESCRIPTION VARCHAR(1024),
+ IS_ENABLED CHAR(1) NOT NULL DEFAULT '0',
+ PRIMARY KEY (ID),
+ UNIQUE (TENANT_ID, NAME),
+ UNIQUE (UUID)
+);
+
+CREATE TABLE IF NOT EXISTS IDVP_CLAIM_MAPPING (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ IDVP_ID INTEGER NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ CLAIM VARCHAR(254),
+ LOCAL_CLAIM VARCHAR(254),
+ PRIMARY KEY (ID),
+ UNIQUE (IDVP_ID, CLAIM, TENANT_ID),
+ FOREIGN KEY (IDVP_ID) REFERENCES IDVP(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDVP_CONFIG (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ IDVP_ID INTEGER NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ PROPERTY_KEY VARCHAR(254) NOT NULL,
+ PROPERTY_VALUE VARCHAR(1024),
+ IS_SECRET CHAR (1) DEFAULT '0',
+ PRIMARY KEY (ID),
+ UNIQUE (IDVP_ID, PROPERTY_KEY, TENANT_ID),
+ FOREIGN KEY (IDVP_ID) REFERENCES IDVP(ID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDV_CLAIM (
+ ID INTEGER NOT NULL AUTO_INCREMENT,
+ UUID CHAR(36) NOT NULL,
+ USER_ID VARCHAR(254) NOT NULL,
+ CLAIM_URI VARCHAR(254),
+ IDVP_ID CHAR(36) NOT NULL,
+ TENANT_ID INTEGER NOT NULL,
+ IS_VERIFIED CHAR(1) NOT NULL DEFAULT '0',
+ METADATA BLOB,
+ PRIMARY KEY (ID),
+ UNIQUE (CLAIM_URI, TENANT_ID, USER_ID, IDVP_ID),
+ UNIQUE (UUID),
+ FOREIGN KEY (IDVP_ID) REFERENCES IDVP(UUID) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS IDN_OAUTH_PAR (
+ REQ_URI_REF VARCHAR(255) PRIMARY KEY,
+ CLIENT_ID VARCHAR(255),
+ SCHEDULED_EXPIRY BIGINT,
+ PARAMETERS MEDIUMTEXT
+);
+
+CREATE TABLE IF NOT EXISTS API_RESOURCE (
+ ID VARCHAR(255) NOT NULL PRIMARY KEY,
+ CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT,
+ NAME VARCHAR(255) NOT NULL,
+ IDENTIFIER VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(255),
+ TYPE VARCHAR(255) NOT NULL,
+ REQUIRES_AUTHORIZATION BOOLEAN NOT NULL,
+ CONSTRAINT IDENTIFIER_UNIQUE UNIQUE (TENANT_ID, IDENTIFIER)
+);
+
+CREATE TABLE IF NOT EXISTS SCOPE (
+ ID VARCHAR(255) NOT NULL PRIMARY KEY,
+ CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT,
+ API_ID VARCHAR(255) NOT NULL,
+ NAME VARCHAR(255) NOT NULL,
+ DISPLAY_NAME VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(300),
+ FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE,
+ CONSTRAINT SCOPE_UNIQUE UNIQUE (TENANT_ID, NAME)
+);
+
+-- --------------------------- INDEX CREATION -----------------------------
+-- IDN_OAUTH2_ACCESS_TOKEN --
+CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED);
+CREATE INDEX IDX_ATH ON IDN_OAUTH2_ACCESS_TOKEN(ACCESS_TOKEN_HASH);
+CREATE INDEX IDX_AT_TI_UD ON IDN_OAUTH2_ACCESS_TOKEN(AUTHZ_USER, TENANT_ID, TOKEN_STATE, USER_DOMAIN);
+CREATE INDEX IDX_AT_AT ON IDN_OAUTH2_ACCESS_TOKEN(ACCESS_TOKEN);
+CREATE INDEX IDX_AT_RTH ON IDN_OAUTH2_ACCESS_TOKEN(REFRESH_TOKEN_HASH);
+CREATE INDEX IDX_AT_RT ON IDN_OAUTH2_ACCESS_TOKEN(REFRESH_TOKEN);
+CREATE INDEX IDX_TBR_TS ON IDN_OAUTH2_ACCESS_TOKEN(TOKEN_BINDING_REF, TOKEN_STATE);
+
+-- IDN_OAUTH2_AUTHORIZATION_CODE --
+CREATE INDEX IDX_AUTHORIZATION_CODE_HASH ON IDN_OAUTH2_AUTHORIZATION_CODE (AUTHORIZATION_CODE_HASH, CONSUMER_KEY_ID);
+CREATE INDEX IDX_AUTHORIZATION_CODE_AU_TI ON IDN_OAUTH2_AUTHORIZATION_CODE (AUTHZ_USER, TENANT_ID, USER_DOMAIN, STATE);
+CREATE INDEX IDX_AC_CKID ON IDN_OAUTH2_AUTHORIZATION_CODE(CONSUMER_KEY_ID);
+CREATE INDEX IDX_AC_TID ON IDN_OAUTH2_AUTHORIZATION_CODE(TOKEN_ID);
+CREATE INDEX IDX_AC_AC_CKID ON IDN_OAUTH2_AUTHORIZATION_CODE(AUTHORIZATION_CODE, CONSUMER_KEY_ID);
+CREATE INDEX IDX_AT_CKID_AU_TID_UD_TSH_TS ON IDN_OAUTH2_ACCESS_TOKEN(CONSUMER_KEY_ID, AUTHZ_USER, TENANT_ID, USER_DOMAIN, TOKEN_SCOPE_HASH, TOKEN_STATE);
+
+-- IDN_SCIM_GROUP --
+CREATE INDEX IDX_IDN_SCIM_GROUP_TI_RN ON IDN_SCIM_GROUP (TENANT_ID, ROLE_NAME);
+CREATE INDEX IDX_IDN_SCIM_GROUP_TI_RN_AN ON IDN_SCIM_GROUP (TENANT_ID, ROLE_NAME, ATTR_NAME);
+
+-- IDN_AUTH_SESSION_STORE --
+CREATE INDEX IDX_IDN_AUTH_SESSION_TIME ON IDN_AUTH_SESSION_STORE (TIME_CREATED);
+CREATE INDEX IDX_IDN_AUTH_SSTR_ST_OP_ID_TM ON IDN_AUTH_SESSION_STORE (OPERATION, SESSION_TYPE, SESSION_ID, TIME_CREATED);
+CREATE INDEX IDX_IDN_AUTH_SSTR_ET_ID ON IDN_AUTH_SESSION_STORE (EXPIRY_TIME, SESSION_ID);
+
+-- IDN_AUTH_TEMP_SESSION_STORE --
+CREATE INDEX IDX_IDN_AUTH_TMP_SESSION_TIME ON IDN_AUTH_TEMP_SESSION_STORE (TIME_CREATED);
+
+-- IDN_OIDC_SCOPE_CLAIM_MAPPING --
+CREATE INDEX IDX_AT_SI_ECI ON IDN_OIDC_SCOPE_CLAIM_MAPPING(SCOPE_ID, EXTERNAL_CLAIM_ID);
+
+-- IDN_OAUTH2_SCOPE --
+CREATE INDEX IDX_SC_TID ON IDN_OAUTH2_SCOPE(TENANT_ID);
+
+-- IDN_OAUTH2_SCOPE_BINDING --
+CREATE INDEX IDX_SB_SCPID ON IDN_OAUTH2_SCOPE_BINDING(SCOPE_ID);
+
+-- IDN_OIDC_REQ_OBJECT_REFERENCE --
+CREATE INDEX IDX_OROR_TID ON IDN_OIDC_REQ_OBJECT_REFERENCE(TOKEN_ID);
+
+-- IDN_OAUTH2_ACCESS_TOKEN_SCOPE --
+CREATE INDEX IDX_ATS_TID ON IDN_OAUTH2_ACCESS_TOKEN_SCOPE(TOKEN_ID);
+
+-- SP_TEMPLATE --
+CREATE INDEX IDX_SP_TEMPLATE ON SP_TEMPLATE (TENANT_ID, NAME);
+
+-- IDN_AUTH_USER --
+CREATE INDEX IDX_AUTH_USER_UN_TID_DN ON IDN_AUTH_USER (USER_NAME, TENANT_ID, DOMAIN_NAME);
+CREATE INDEX IDX_AUTH_USER_DN_TOD ON IDN_AUTH_USER (DOMAIN_NAME, TENANT_ID);
+
+-- IDN_AUTH_USER_SESSION_MAPPING --
+CREATE INDEX IDX_USER_ID ON IDN_AUTH_USER_SESSION_MAPPING (USER_ID);
+CREATE INDEX IDX_SESSION_ID ON IDN_AUTH_USER_SESSION_MAPPING (SESSION_ID);
+
+-- IDN_AUTH_SESSION_APP_INFO --
+CREATE INDEX IDX_AUTH_SAI_UN_AID_SID ON IDN_AUTH_SESSION_APP_INFO (APP_ID, SUBJECT, SESSION_ID);
+
+-- IDN_OAUTH_CONSUMER_APPS --
+CREATE INDEX IDX_OCA_UM_TID_UD_APN ON IDN_OAUTH_CONSUMER_APPS(USERNAME,TENANT_ID,USER_DOMAIN, APP_NAME);
+
+-- IDX_SPI_APP --
+CREATE INDEX IDX_SPI_APP ON SP_INBOUND_AUTH(APP_ID);
+
+-- IDN_OIDC_PROPERTY --
+CREATE INDEX IDX_IOP_CK ON IDN_OIDC_PROPERTY(CONSUMER_KEY);
+
+-- IDN_FIDO2_PROPERTY --
+CREATE INDEX IDX_FIDO2_STR ON FIDO2_DEVICE_STORE(USER_NAME, TENANT_ID, DOMAIN_NAME, CREDENTIAL_ID, USER_HANDLE);
+
+-- IDN_ASSOCIATED_ID --
+CREATE INDEX IDX_AI_DN_UN_AI ON IDN_ASSOCIATED_ID(DOMAIN_NAME, USER_NAME, ASSOCIATION_ID);
+
+-- IDN_OAUTH2_TOKEN_BINDING --
+CREATE INDEX IDX_IDN_AUTH_BIND ON IDN_OAUTH2_TOKEN_BINDING (TOKEN_BINDING_REF);
+CREATE INDEX IDX_TK_VALUE_TYPE ON IDN_OAUTH2_TOKEN_BINDING (TOKEN_BINDING_VALUE, TOKEN_BINDING_TYPE);
+
+-- IDN_FED_AUTH_SESSION_MAPPING --
+CREATE INDEX IDX_FEDERATED_AUTH_SESSION_ID ON IDN_FED_AUTH_SESSION_MAPPING (SESSION_ID);
+
+-- IDN_REMOTE_FETCH_REVISIONS --
+CREATE INDEX IDX_REMOTE_FETCH_REVISION_CONFIG_ID ON IDN_REMOTE_FETCH_REVISIONS (CONFIG_ID);
+
+-- IDN_CORS_ASSOCIATION --
+CREATE INDEX IDX_CORS_SP_APP_ID ON IDN_CORS_ASSOCIATION (SP_APP_ID);
+
+-- IDN_CORS_ASSOCIATION --
+CREATE INDEX IDX_CORS_ORIGIN_ID ON IDN_CORS_ASSOCIATION (IDN_CORS_ORIGIN_ID);
+
+-- IDN_SECRET --
+CREATE INDEX IDN_SECRET_TYPE_ID ON IDN_SECRET (TYPE_ID);
+
+-- IDN_CLAIM --
+CREATE INDEX IDX_CLAIM_TI_CU ON IDN_CLAIM (TENANT_ID, CLAIM_URI);
+
+-- IDP_AUTHENTICATOR_PROPERTY --
+CREATE INDEX IDX_AUTH_PROP_AUTH_ID ON IDP_AUTHENTICATOR_PROPERTY (AUTHENTICATOR_ID);
+
+-- IDN_CONFIG_FILE --
+CREATE INDEX IDX_CON_FILE_RES_ID ON IDN_CONFIG_FILE (RESOURCE_ID);
+
+-- SCOPE --
+CREATE INDEX API_ID_NAME_INDEX ON SCOPE (API_ID, NAME);
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/repository/conf/carbon.xml b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/repository/conf/carbon.xml
new file mode 100755
index 000000000000..4a8c851d3d97
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/repository/conf/carbon.xml
@@ -0,0 +1,686 @@
+
+
+
+
+
+
+
+ WSO2 Identity Server
+
+
+ IS
+
+
+ 5.3.0
+
+
+ localhost
+
+
+ localhost
+
+
+ local:/${carbon.context}/services/
+
+
+
+
+
+
+ IdentityServer
+
+
+
+
+
+
+ org.wso2.carbon
+
+
+ /
+
+
+
+
+
+
+
+
+ 15
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+ 9999
+
+ 11111
+
+
+
+
+
+ 10389
+
+ 8000
+
+
+
+
+
+ 10500
+
+
+
+
+
+
+
+
+ org.wso2.carbon.tomcat.jndi.CarbonJavaURLContextFactory
+
+
+
+
+
+
+
+
+ java
+
+
+
+
+
+
+
+
+
+ false
+
+
+ false
+
+
+ 600
+
+
+
+ false
+
+
+
+
+
+
+
+ 30
+
+
+
+
+
+
+
+
+ 15
+
+
+
+
+
+ ${carbon.home}/repository/deployment/server/
+
+
+ 15
+
+
+ ${carbon.home}/repository/conf/axis2/axis2.xml
+
+
+ 30000
+
+
+ ${carbon.home}/repository/deployment/client/
+
+ ${carbon.home}/repository/conf/axis2/axis2_client.xml
+
+ true
+
+
+
+
+
+
+
+
+
+ admin
+ Default Administrator Role
+
+
+ user
+ Default User Role
+
+
+
+
+
+
+
+
+
+
+
+ ${carbon.home}/repository/resources/security/wso2carbon.jks
+
+ JKS
+
+ wso2carbon
+
+ wso2carbon
+
+ wso2carbon
+
+
+
+
+
+ ${carbon.home}/repository/resources/security/client-truststore.jks
+
+ JKS
+
+ wso2carbon
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserManager
+
+
+ false
+
+ org.wso2.carbon.identity.provider.AttributeCallbackHandler
+
+
+ org.wso2.carbon.identity.sts.store.DBTokenStore
+
+
+ true
+ allow
+
+
+
+
+
+
+ claim_mgt_menu
+ identity_mgt_emailtemplate_menu
+ identity_security_questions_menu
+
+
+
+ ${carbon.home}/tmp/work
+
+
+
+
+
+ true
+
+
+ 10
+
+
+ 30
+
+
+
+
+
+ 100
+
+
+
+ keystore
+ certificate
+ *
+
+ org.wso2.carbon.ui.transports.fileupload.AnyFileUploadExecutor
+
+
+
+
+ jarZip
+
+ org.wso2.carbon.ui.transports.fileupload.JarZipUploadExecutor
+
+
+
+ dbs
+
+ org.wso2.carbon.ui.transports.fileupload.DBSFileUploadExecutor
+
+
+
+ tools
+
+ org.wso2.carbon.ui.transports.fileupload.ToolsFileUploadExecutor
+
+
+
+ toolsAny
+
+ org.wso2.carbon.ui.transports.fileupload.ToolsAnyFileUploadExecutor
+
+
+
+
+
+
+
+
+
+ - info
+ org.wso2.carbon.core.transports.util.InfoProcessor
+
+
+ - wsdl
+ org.wso2.carbon.core.transports.util.Wsdl11Processor
+
+
+ - wsdl2
+ org.wso2.carbon.core.transports.util.Wsdl20Processor
+
+
+ - xsd
+ org.wso2.carbon.core.transports.util.XsdProcessor
+
+
+
+
+
+ false
+ false
+ true
+ svn
+ http://svnrepo.example.com/repos/
+ username
+ password
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${require.carbon.servlet}
+
+
+
+
+ true
+
+
+
+
+
+
+ default repository
+ http://product-dist.wso2.com/p2/carbon/releases/wilkes/
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+ true
+
+
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/repository/conf/identity/identity.xml b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/repository/conf/identity/identity.xml
new file mode 100644
index 000000000000..27d162bf120f
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/repository/conf/identity/identity.xml
@@ -0,0 +1,746 @@
+
+
+
+
+
+
+
+
+ jdbc/WSO2IdentityDB
+
+
+
+
+ true
+ true
+ 0
+
+ true
+ 20160
+ 1140
+
+
+ true
+ 720
+
+
+
+
+
+
+ 15
+ 20160
+
+
+
+
+
+ ${carbon.home}/conf/keystores
+ SunX509
+ SunX509
+
+
+
+ SelfAndManaged
+ CertValidate
+
+
+
+
+
+
+
+
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/openidserver
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/openid
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/openid_login.do
+
+
+ false
+
+ 7200
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -1
+ -1
+ -1
+ -1
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth/request-token
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth/authorize-url
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth/access-token
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/authorize
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/token
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/revoke
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/introspect
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/userinfo
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oidc/checksession
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oidc/logout
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_authz.do
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_error.do
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_consent.do
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_logout_consent.do
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/oauth2_logout.do
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/.well-known/webfinger
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/identity/connect/register
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/jwks
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/oidcdiscovery
+
+
+ 300
+
+ 3600
+
+ 3600
+
+ 84600
+
+ 300
+
+ false
+
+ true
+
+ org.wso2.carbon.identity.oauth.tokenprocessor.PlainTextPersistenceProcessor
+
+
+
+ false
+
+
+
+
+
+ token
+ org.wso2.carbon.identity.oauth2.authz.handlers.AccessTokenResponseTypeHandler
+
+
+ code
+ org.wso2.carbon.identity.oauth2.authz.handlers.CodeResponseTypeHandler
+
+
+ id_token
+ org.wso2.carbon.identity.oauth2.authz.handlers.IDTokenResponseTypeHandler
+
+
+ id_token token
+ org.wso2.carbon.identity.oauth2.authz.handlers.IDTokenTokenResponseTypeHandler
+
+
+
+
+
+ authorization_code
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.AuthorizationCodeGrantHandler
+
+
+ password
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.PasswordGrantHandler
+
+
+ refresh_token
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.RefreshGrantHandler
+
+
+ client_credentials
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.ClientCredentialsGrantHandler
+
+
+ urn:ietf:params:oauth:grant-type:saml2-bearer
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.saml.SAML2BearerGrantHandler
+
+
+ iwa:ntlm
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.iwa.ntlm.NTLMAuthenticationGrantHandler
+
+
+ idTokenNotAllowedGrantType
+ org.wso2.carbon.identity.oauth2.token.handlers.grant.idTokenNotAllowedGrantHandler
+ false
+
+
+
+
+
+
+
+
+ false
+
+
+
+ false
+
+
+
+ false
+ org.wso2.carbon.identity.oauth2.authcontext.JWTTokenGenerator
+ org.wso2.carbon.identity.oauth2.authcontext.DefaultClaimsRetriever
+ http://wso2.org/claims
+ SHA256withRSA
+ 10
+
+
+
+
+
+ org.wso2.carbon.identity.openidconnect.DefaultIDTokenBuilder
+ SHA256withRSA
+
+
+
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/token
+ org.wso2.carbon.identity.openidconnect.DefaultOIDCClaimsCallbackHandler
+ 3600
+ org.wso2.carbon.identity.oauth.endpoint.user.impl.UserInfoUserStoreClaimRetriever
+ org.wso2.carbon.identity.oauth.endpoint.user.impl.UserInforRequestDefaultValidator
+ org.wso2.carbon.identity.oauth.endpoint.user.impl.UserInfoISAccessTokenValidator
+ org.wso2.carbon.identity.oauth.endpoint.user.impl.UserInfoJSONResponseBuilder
+ false
+
+
+
+
+
+
+
+ gtalk
+ talk.google.com
+ 5222
+ gmail.com
+ multifactor1@gmail.com
+ wso2carbon
+
+
+
+
+
+ 157680000
+ 157680000
+ ${carbon.host}
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/samlsso
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/samlsso_logout.do
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/samlsso_notification.do
+ 5
+ 60000
+
+ false
+ http://wso2.org/claims
+ org.wso2.carbon.identity.sso.saml.builders.assertion.ExtendedDefaultAssertionBuilder
+
+ org.wso2.carbon.identity.sso.saml.builders.encryption.DefaultSSOEncrypter
+ org.wso2.carbon.identity.sso.saml.builders.signature.DefaultSSOSigner
+ org.wso2.carbon.identity.sso.saml.validators.SAML2HTTPRedirectDeflateSignatureValidator
+
+
+
+ 5
+ false
+ http://www.w3.org/2000/09/xmldsig#rsa-sha1
+ http://www.w3.org/2000/09/xmldsig#sha1
+ true
+
+
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/services/wso2carbon-sts
+
+
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/passivests
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/authenticationendpoint/retry.do
+ org.wso2.carbon.identity.sts.passive.utils.NoPersistenceTokenStore
+ true
+
+
+
+
+ false
+ ${Ports.ThriftEntitlementReceivePort}
+ 10000
+
+ ${carbon.home}/repository/resources/security/wso2carbon.jks
+ wso2carbon
+
+
+ ${carbon.host}
+
+
+
+
+
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/wso2/scim/Users
+ ${carbon.protocol}://${carbon.host}:${carbon.management.port}/wso2/scim/Groups
+
+
+ 5
+
+
+ 10
+ local://services
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.wso2.carbon.identity.governance.store.JDBCIdentityDataStore
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /permission/admin/manage/identity/identitymgt
+
+
+
+
+
+ /permission/admin/manage/identity/usermgt/view
+
+
+ /permission/admin/manage/identity/usermgt/view
+
+
+
+ /permission/admin/manage/identity/configmgt/list
+
+
+
+ /permission/admin/manage/identity/configmgt/add
+
+
+ /permission/admin/manage/identity/configmgt/update
+
+
+
+ /permission/admin/manage/identity/configmgt/delete
+
+
+
+ /permission/admin/manage/identity/configmgt/add
+
+
+ /permission/admin/manage/identity/configmgt/update
+
+
+
+ /permission/admin/manage/identity/configmgt/delete
+
+
+
+ /permission/admin/manage/identity/configmgt/add
+
+
+ /permission/admin/manage/identity/configmgt/update
+
+
+
+ /permission/admin/manage/identity/configmgt/delete
+
+
+
+
+
+
+ /permission/admin/manage/identity/consentmgt/add
+
+
+
+ /permission/admin/manage/identity/consentmgt/delete
+
+
+
+ /permission/admin/manage/identity/consentmgt/add
+
+
+
+ /permission/admin/manage/identity/consentmgt/delete
+
+
+
+ /permission/admin/manage/identity/consentmgt/add
+
+
+
+ /permission/admin/manage/identity/consentmgt/delete
+
+
+
+ /permission/admin/manage/identity/identitymgt
+
+
+
+ /permission/admin/manage/identity/applicationmgt/create
+
+
+ /permission/admin/manage/identity/applicationmgt/delete
+
+
+ /permission/admin/manage/identity/applicationmgt/update
+
+
+ /permission/admin/manage/identity/applicationmgt/view
+
+
+ /permission/admin/manage/identity/applicationmgt/delete
+
+
+ /permission/admin/manage/identity/applicationmgt/create
+
+
+ /permission/admin/manage/identity/applicationmgt/view
+
+
+ /permission/admin/manage/identity/pep
+
+
+ /permission/admin/manage/identity/usermgt/create
+
+
+ /permission/admin/manage/identity/usermgt/list
+
+
+ /permission/admin/manage/identity/rolemgt/create
+
+
+ /permission/admin/manage/identity/rolemgt/view
+
+
+ /permission/admin/manage/identity/usermgt/view
+
+
+ /permission/admin/manage/identity/usermgt/update
+
+
+ /permission/admin/manage/identity/usermgt/update
+
+
+ /permission/admin/manage/identity/usermgt/delete
+
+
+ /permission/admin/manage/identity/rolemgt/view
+
+
+ /permission/admin/manage/identity/rolemgt/update
+
+
+ /permission/admin/manage/identity/rolemgt/update
+
+
+ /permission/admin/manage/identity/rolemgt/delete
+
+
+ /permission/admin/login
+
+
+ /permission/admin/manage/identity/usermgt/delete
+
+
+ /permission/admin/login
+
+
+ /permission/admin/login
+
+
+ /permission/admin/manage/identity/usermgt/create
+
+
+
+
+
+
+
+
+ /permission/admin/manage/identity/usermgt
+
+
+ /permission/admin/manage/identity/applicationmgt
+
+
+
+
+
+
+ /permission/admin/manage/identity/usermgt/update
+
+
+
+
+
+ /permission/admin/manage/humantask/viewtasks
+
+
+ /permission/admin/login
+
+
+ /permission/admin/manage/identity/usermgt
+
+
+ /permission/admin/manage/identity/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /api/identity/user/v0.9
+ /api/identity/recovery/v0.9
+ /oauth2
+ /api/identity/entitlement
+
+
+ /identity/(.*)
+
+
+
+
+
+ applications,identity-providers
+
+
+
+ 300
+
diff --git a/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/testng.xml b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/testng.xml
new file mode 100644
index 000000000000..223d2301508c
--- /dev/null
+++ b/components/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt/src/test/resources/testng.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/api-resource-mgt/pom.xml b/components/api-resource-mgt/pom.xml
new file mode 100644
index 000000000000..927fd222c410
--- /dev/null
+++ b/components/api-resource-mgt/pom.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+ org.wso2.carbon.identity.framework
+ identity-framework
+ 5.25.319-SNAPSHOT
+ ../../pom.xml
+
+
+ 4.0.0
+ api-resource-mgt
+ pom
+ WSO2 Carbon - API Resource Management Aggregator Module
+
+ This is a Carbon bundle that represent the API Resource Management Aggregator Module.
+
+ http://wso2.org
+
+
+ org.wso2.carbon.identity.api.resource.mgt
+
+
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
new file mode 100644
index 000000000000..1b678025f795
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/APIResource.java
@@ -0,0 +1,200 @@
+/*
+ * 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;
+
+import java.util.List;
+
+/**
+ * API resource.
+ */
+public class APIResource {
+
+ private String id;
+ private Integer cursorKey;
+ private String name;
+ private String type;
+ private String identifier;
+ private String description;
+ private Integer tenantId;
+ private boolean requiresAuthorization;
+ private List scopes;
+ private List subscribedApplications;
+
+ public APIResource() {
+ }
+
+ public APIResource(APIResourceBuilder apiResourceBuilder) {
+
+ this.id = apiResourceBuilder.id;
+ this.cursorKey = apiResourceBuilder.cursorKey;
+ this.name = apiResourceBuilder.name;
+ this.type = apiResourceBuilder.type;
+ this.identifier = apiResourceBuilder.identifier;
+ this.description = apiResourceBuilder.description;
+ this.tenantId = apiResourceBuilder.tenantId;
+ this.requiresAuthorization = apiResourceBuilder.requiresAuthorization;
+ this.scopes = apiResourceBuilder.scopes;
+ this.subscribedApplications = apiResourceBuilder.subscribedApplications;
+ }
+
+ public String getId() {
+
+ return id;
+ }
+
+ public Integer getCursorKey() {
+
+ return cursorKey;
+ }
+
+ public String getName() {
+
+ return name;
+ }
+
+ public String getType() {
+
+ return type;
+ }
+
+ public String getIdentifier() {
+
+ return identifier;
+ }
+
+ public String getDescription() {
+
+ return description;
+ }
+
+ public Integer getTenantId() {
+
+ return tenantId;
+ }
+
+ public boolean isRequiresAuthorization() {
+
+ return requiresAuthorization;
+ }
+
+ public List getScopes() {
+
+ return scopes;
+ }
+
+ public void setScopes(List scopes) {
+
+ this.scopes = scopes;
+ }
+
+ public List getSubscribedApplications() {
+
+ return subscribedApplications;
+ }
+
+ public void setSubscribedApplications(List subscribedApplications) {
+
+ this.subscribedApplications = subscribedApplications;
+ }
+
+ /**
+ * API resource builder.
+ */
+ public static class APIResourceBuilder {
+
+ private String id;
+ private Integer cursorKey;
+ private String name;
+ private String type;
+ private String identifier;
+ private String description;
+ private Integer tenantId;
+ private boolean requiresAuthorization;
+ private List scopes;
+ private List subscribedApplications;
+
+ public APIResourceBuilder() {
+ }
+
+ public APIResourceBuilder name(String name) {
+
+ this.name = name;
+ return this;
+ }
+
+ public APIResourceBuilder identifier(String identifier) {
+
+ this.identifier = identifier;
+ return this;
+ }
+
+ public APIResourceBuilder id(String id) {
+
+ this.id = id;
+ return this;
+ }
+
+ public APIResourceBuilder cursorKey(Integer cursorKey) {
+
+ this.cursorKey = cursorKey;
+ return this;
+ }
+
+ public APIResourceBuilder type(String type) {
+
+ this.type = type;
+ return this;
+ }
+
+ public APIResourceBuilder description(String description) {
+
+ this.description = description;
+ return this;
+ }
+
+ public APIResourceBuilder tenantId(Integer tenantId) {
+
+ this.tenantId = tenantId;
+ return this;
+ }
+
+ public APIResourceBuilder requiresAuthorization(boolean requiresAuthorization) {
+
+ this.requiresAuthorization = requiresAuthorization;
+ return this;
+ }
+
+ public APIResourceBuilder scopes(List scopes) {
+
+ this.scopes = scopes;
+ return this;
+ }
+
+ public APIResourceBuilder subscribedApplications(List subscribedApplications) {
+
+ this.subscribedApplications = subscribedApplications;
+ 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/Scope.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/Scope.java
new file mode 100644
index 000000000000..853ac0076c39
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/Scope.java
@@ -0,0 +1,152 @@
+/*
+ * 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;
+
+/**
+ * Represents a scope.
+ */
+public class Scope {
+
+ private String id;
+ private String name;
+ private String displayName;
+ private String description;
+ private String apiID;
+ private String orgID;
+
+ public Scope() {
+
+ }
+
+ public Scope(String scopeId, String scopeQualifiedName, String scopeDisplayName, String scopeDescription) {
+
+ this.id = scopeId;
+ this.name = scopeQualifiedName;
+ this.displayName = scopeDisplayName;
+ this.description = scopeDescription;
+ }
+
+ public Scope(String scopeId, String scopeQualifiedName, String scopeDisplayName, String scopeDescription,
+ String apiID, String orgID) {
+
+ this.id = scopeId;
+ this.name = scopeQualifiedName;
+ this.displayName = scopeDisplayName;
+ this.description = scopeDescription;
+ this.apiID = apiID;
+ this.orgID = orgID;
+ }
+
+ public String getId() {
+
+ return id;
+ }
+
+ public String getName() {
+
+ return name;
+ }
+
+ public String getDisplayName() {
+
+ return displayName;
+ }
+
+ public String getDescription() {
+
+ return description;
+ }
+
+ public String getApiID() {
+
+ return apiID;
+ }
+
+ public String getOrgID() {
+
+ return orgID;
+ }
+
+ /**
+ * Scope builder.
+ */
+ public static class ScopeBuilder {
+
+ private String id;
+ private String name;
+ private String displayName;
+ private String description;
+ private String apiID;
+ private String orgID;
+
+ public ScopeBuilder() {
+
+ }
+
+ public ScopeBuilder(String scopeId, String scopeQualifiedName, String scopeDisplayName,
+ String scopeDescription) {
+
+ this.id = scopeId;
+ this.name = scopeQualifiedName;
+ this.displayName = scopeDisplayName;
+ this.description = scopeDescription;
+ }
+
+ public ScopeBuilder id(String id) {
+
+ this.id = id;
+ return this;
+ }
+
+ public ScopeBuilder name(String name) {
+
+ this.name = name;
+ return this;
+ }
+
+ public ScopeBuilder displayName(String displayName) {
+
+ this.displayName = displayName;
+ return this;
+ }
+
+ public ScopeBuilder description(String description) {
+
+ this.description = description;
+ return this;
+ }
+
+ public ScopeBuilder apiID(String apiID) {
+
+ this.apiID = apiID;
+ return this;
+ }
+
+ public ScopeBuilder orgID(String orgID) {
+
+ this.orgID = orgID;
+ return this;
+ }
+
+ public Scope build() {
+
+ return new Scope(id, name, displayName, description, apiID, orgID);
+ }
+ }
+}
diff --git a/features/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt.server.feature/pom.xml b/features/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt.server.feature/pom.xml
new file mode 100644
index 000000000000..37bf52c856f9
--- /dev/null
+++ b/features/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt.server.feature/pom.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+ org.wso2.carbon.identity.framework
+ api-resource-management-feature
+ 5.25.319-SNAPSHOT
+ ../pom.xml
+
+
+ 4.0.0
+ org.wso2.carbon.identity.api.resource.mgt.server.feature
+ pom
+ API Resource Server Feature
+ http://wso2.org
+ This feature contains the core bundles required for API Resource Management Framework
+
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.api.resource.mgt
+
+
+
+
+
+
+ org.wso2.maven
+ carbon-p2-plugin
+ ${carbon.p2.plugin.version}
+
+
+ 4-p2-feature-generation
+ package
+
+ p2-feature-gen
+
+
+ org.wso2.carbon.identity.api.resource.mgt.server
+ ../../etc/feature.properties
+
+
+ org.wso2.carbon.p2.category.type:server
+
+
+
+ org.wso2.carbon.identity.framework:org.wso2.carbon.identity.api.resource.mgt
+
+
+
+
+
+
+
+
+
diff --git a/features/api-resource-mgt/pom.xml b/features/api-resource-mgt/pom.xml
new file mode 100644
index 000000000000..ae91b9f577af
--- /dev/null
+++ b/features/api-resource-mgt/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+ org.wso2.carbon.identity.framework
+ identity-framework
+ 5.25.319-SNAPSHOT
+ ../../pom.xml
+
+
+ 4.0.0
+ api-resource-management-feature
+ pom
+ WSO2 Carbon - API Resource Management Feature
+ http://wso2.org
+
+
+ org.wso2.carbon.identity.api.resource.mgt.server.feature
+
+
+
+
diff --git a/features/authentication-framework/org.wso2.carbon.identity.application.authentication.framework.server.feature/resources/org.wso2.carbon.identity.application.authentication.framework.server.feature.default.json b/features/authentication-framework/org.wso2.carbon.identity.application.authentication.framework.server.feature/resources/org.wso2.carbon.identity.application.authentication.framework.server.feature.default.json
index 7d848d3ea173..baf4cb91aa3f 100644
--- a/features/authentication-framework/org.wso2.carbon.identity.application.authentication.framework.server.feature/resources/org.wso2.carbon.identity.application.authentication.framework.server.feature.default.json
+++ b/features/authentication-framework/org.wso2.carbon.identity.application.authentication.framework.server.feature/resources/org.wso2.carbon.identity.application.authentication.framework.server.feature.default.json
@@ -233,5 +233,10 @@
"http://wso2.org/claims/created",
"http://wso2.org/claims/modified",
"http://wso2.org/claims/groups"
+ ],
+ "oauth.restricted_scopes": [
+ "internal",
+ "console",
+ "SYSTEM"
]
}
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 40a5a79c0393..300ff5db28ab 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
@@ -1933,6 +1933,54 @@ CREATE TABLE IDN_ORG_USER_ASSOCIATION (
PRIMARY KEY (SHARED_USER_ID, SUB_ORG_ID, REAL_USER_ID, USER_RESIDENT_ORG_ID)
)
/
+CREATE TABLE API_RESOURCE (
+ ID VARCHAR(255) PRIMARY KEY NOT NULL,
+ CURSOR_KEY INTEGER NOT NULL,
+ NAME VARCHAR(255) NOT NULL,
+ IDENTIFIER VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(255),
+ TYPE VARCHAR(255) NOT NULL,
+ REQUIRES_AUTHORIZATION CHAR(1) NOT NULL,
+ CONSTRAINT IDENTIFIER_UNIQUE UNIQUE (IDENTIFIER, TENANT_ID)
+)
+/
+CREATE SEQUENCE API_RESOURCE_SEQ START WITH 1 INCREMENT BY 1 NOCACHE
+/
+CREATE TRIGGER API_RESOURCE_TRIG NO CASCADE
+ BEFORE INSERT
+ ON API_RESOURCE
+ REFERENCING NEW AS NEW
+ FOR EACH ROW MODE DB2SQL
+ BEGIN ATOMIC
+ SET (NEW.CURSOR_KEY) = (NEXTVAL FOR API_RESOURCE_SEQ);
+ END
+/
+
+CREATE TABLE SCOPE (
+ ID VARCHAR(255) NOT NULL PRIMARY KEY,
+ CURSOR_KEY INTEGER NOT NULL,
+ API_ID VARCHAR(255) NOT NULL,
+ NAME VARCHAR(255) NOT NULL,
+ DISPLAY_NAME VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(300),
+ FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE,
+ CONSTRAINT SCOPE_UNIQUE UNIQUE (NAME, TENANT_ID)
+)
+/
+CREATE SEQUENCE SCOPE_SEQ START WITH 1 INCREMENT BY 1 NOCACHE
+/
+CREATE TRIGGER SCOPE_TRIG NO CASCADE
+ BEFORE INSERT
+ ON SCOPE
+ REFERENCING NEW AS NEW
+ FOR EACH ROW MODE DB2SQL
+ BEGIN ATOMIC
+ SET (NEW.CURSOR_KEY) = (NEXTVAL FOR SCOPE_SEQ);
+ END
+/
+
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED)
@@ -2082,3 +2130,6 @@ CREATE INDEX IDX_AUTH_PROP_AUTH_ID ON IDP_AUTHENTICATOR_PROPERTY (AUTHENTICATOR_
-- IDN_CONFIG_FILE --
CREATE INDEX IDX_CON_FILE_RES_ID ON IDN_CONFIG_FILE (RESOURCE_ID);
+
+-- SCOPE --
+CREATE INDEX API_ID_NAME_INDEX ON SCOPE (API_ID, NAME);
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 9ab17fe864e2..18a05eb14c42 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
@@ -1295,6 +1295,30 @@ CREATE TABLE IF NOT EXISTS IDN_ORG_USER_ASSOCIATION(
PRIMARY KEY (SHARED_USER_ID, SUB_ORG_ID, REAL_USER_ID, USER_RESIDENT_ORG_ID)
);
+CREATE TABLE IF NOT EXISTS API_RESOURCE (
+ ID VARCHAR(255) NOT NULL PRIMARY KEY,
+ CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT,
+ NAME VARCHAR(255) NOT NULL,
+ IDENTIFIER VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(255),
+ TYPE VARCHAR(255) NOT NULL,
+ REQUIRES_AUTHORIZATION BOOLEAN NOT NULL,
+ CONSTRAINT IDENTIFIER_UNIQUE UNIQUE (IDENTIFIER, TENANT_ID)
+);
+
+CREATE TABLE IF NOT EXISTS SCOPE (
+ ID VARCHAR(255) NOT NULL PRIMARY KEY,
+ CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT,
+ API_ID VARCHAR(255) NOT NULL,
+ NAME VARCHAR(255) NOT NULL,
+ DISPLAY_NAME VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(300),
+ FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE,
+ CONSTRAINT SCOPE_UNIQUE UNIQUE (NAME, TENANT_ID)
+);
+
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED);
@@ -1396,3 +1420,6 @@ CREATE INDEX IDX_AUTH_PROP_AUTH_ID ON IDP_AUTHENTICATOR_PROPERTY (AUTHENTICATOR_
-- IDN_CONFIG_FILE --
CREATE INDEX IDX_CON_FILE_RES_ID ON IDN_CONFIG_FILE (RESOURCE_ID);
+
+-- SCOPE --
+CREATE INDEX API_ID_NAME_INDEX ON SCOPE (API_ID, NAME);
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 aa285bd5117a..4dee8a045e20 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
@@ -1435,6 +1435,32 @@ CREATE TABLE IDN_ORG_USER_ASSOCIATION (
CONSTRAINT PK_ORG_USER_ASSOC PRIMARY KEY (SHARED_USER_ID, SUB_ORG_ID, REAL_USER_ID, USER_RESIDENT_ORG_ID)
);
+IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[API_RESOURCE]') AND TYPE IN (N'U'))
+CREATE TABLE API_RESOURCE (
+ ID VARCHAR(255) NOT NULL PRIMARY KEY,
+ CURSOR_KEY INTEGER NOT NULL IDENTITY,
+ NAME VARCHAR(255) NOT NULL,
+ IDENTIFIER VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(255),
+ TYPE VARCHAR(255) NOT NULL,
+ REQUIRES_AUTHORIZATION BIT NOT NULL,
+ CONSTRAINT IDENTIFIER_UNIQUE UNIQUE (IDENTIFIER, TENANT_ID)
+);
+
+IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[SCOPE]') AND TYPE IN (N'U'))
+CREATE TABLE SCOPE (
+ ID VARCHAR(255) NOT NULL PRIMARY KEY,
+ CURSOR_KEY INTEGER NOT NULL IDENTITY,
+ API_ID VARCHAR(255) NOT NULL,
+ NAME VARCHAR(255) NOT NULL,
+ DISPLAY_NAME VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(300),
+ FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE,
+ CONSTRAINT SCOPE_UNIQUE UNIQUE (NAME, TENANT_ID)
+);
+
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED);
@@ -1538,6 +1564,9 @@ CREATE INDEX IDX_AUTH_PROP_AUTH_ID ON IDP_AUTHENTICATOR_PROPERTY (AUTHENTICATOR_
-- IDN_CONFIG_FILE --
CREATE INDEX IDX_CON_FILE_RES_ID ON IDN_CONFIG_FILE (RESOURCE_ID);
+-- SCOPE --
+CREATE INDEX API_ID_NAME_INDEX ON SCOPE (API_ID, NAME);
+
GO
-- Trigger IDN_CLAIM delete by dialect on IDN_CLAIM_DIALECT deletion --
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 9fb8b673a291..cf6082a6e653 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
@@ -1465,6 +1465,30 @@ CREATE TABLE IDN_ORG_USER_ASSOCIATION(
PRIMARY KEY (SHARED_USER_ID, SUB_ORG_ID, REAL_USER_ID, USER_RESIDENT_ORG_ID)
)ENGINE NDB;
+CREATE TABLE IF NOT EXISTS API_RESOURCE (
+ ID VARCHAR(255) NOT NULL PRIMARY KEY,
+ CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT,
+ NAME VARCHAR(255) NOT NULL,
+ IDENTIFIER VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(255),
+ TYPE VARCHAR(255) NOT NULL,
+ REQUIRES_AUTHORIZATION BOOLEAN NOT NULL,
+ CONSTRAINT IDENTIFIER_UNIQUE UNIQUE (IDENTIFIER, TENANT_ID)
+)ENGINE NDB;
+
+CREATE TABLE IF NOT EXISTS SCOPE (
+ ID VARCHAR(255) NOT NULL PRIMARY KEY,
+ CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT,
+ API_ID VARCHAR(255) NOT NULL,
+ NAME VARCHAR(255) NOT NULL,
+ DISPLAY_NAME VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(300),
+ FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE,
+ CONSTRAINT SCOPE_UNIQUE UNIQUE (NAME, TENANT_ID)
+)ENGINE NDB;
+
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
CREATE INDEX IDX_TC
@@ -1595,3 +1619,6 @@ CREATE INDEX IDX_AUTH_PROP_AUTH_ID ON IDP_AUTHENTICATOR_PROPERTY (AUTHENTICATOR_
-- IDN_CONFIG_FILE --
CREATE INDEX IDX_CON_FILE_RES_ID ON IDN_CONFIG_FILE (RESOURCE_ID);
+
+-- SCOPE --
+CREATE INDEX API_ID_NAME_INDEX ON SCOPE (API_ID, NAME);
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 70d69933d582..8cd252591678 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
@@ -1321,6 +1321,29 @@ CREATE TABLE IDN_ORG_USER_ASSOCIATION(
PRIMARY KEY (SHARED_USER_ID, SUB_ORG_ID, REAL_USER_ID, USER_RESIDENT_ORG_ID)
)ENGINE INNODB;
+CREATE TABLE IF NOT EXISTS API_RESOURCE (
+ ID VARCHAR(255) NOT NULL PRIMARY KEY,
+ CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT,
+ NAME VARCHAR(255) NOT NULL,
+ IDENTIFIER VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(255),
+ TYPE VARCHAR(255) NOT NULL,
+ REQUIRES_AUTHORIZATION BOOLEAN NOT NULL,
+ CONSTRAINT IDENTIFIER_UNIQUE UNIQUE (IDENTIFIER, TENANT_ID)
+)ENGINE INNODB;
+
+CREATE TABLE IF NOT EXISTS SCOPE (
+ ID VARCHAR(255) NOT NULL PRIMARY KEY,
+ CURSOR_KEY INTEGER NOT NULL AUTO_INCREMENT,
+ API_ID VARCHAR(255) NOT NULL,
+ NAME VARCHAR(255) NOT NULL,
+ DISPLAY_NAME VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(300),
+ FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE,
+ CONSTRAINT SCOPE_UNIQUE UNIQUE (NAME, TENANT_ID)
+)ENGINE INNODB;
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
@@ -1420,3 +1443,6 @@ CREATE INDEX IDX_AUTH_PROP_AUTH_ID ON IDP_AUTHENTICATOR_PROPERTY (AUTHENTICATOR_
-- IDN_CONFIG_FILE --
CREATE INDEX IDX_CON_FILE_RES_ID ON IDN_CONFIG_FILE (RESOURCE_ID);
+
+-- SCOPE --
+CREATE INDEX API_ID_NAME_INDEX ON SCOPE (API_ID, NAME);
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 a47cc28fe1d0..9641e95e5abf 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
@@ -1539,6 +1539,36 @@ CREATE TABLE IDN_ORG_USER_ASSOCIATION(
PRIMARY KEY (SHARED_USER_ID, SUB_ORG_ID, REAL_USER_ID, USER_RESIDENT_ORG_ID)
);
+DROP TABLE IF EXISTS API_RESOURCE;
+DROP SEQUENCE IF EXISTS API_RESOURCE_SEQ;
+CREATE SEQUENCE API_RESOURCE_SEQ;
+CREATE TABLE API_RESOURCE (
+ ID VARCHAR(255) NOT NULL PRIMARY KEY,
+ CURSOR_KEY SERIAL,
+ NAME VARCHAR(255) NOT NULL,
+ IDENTIFIER VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(255),
+ TYPE VARCHAR(255) NOT NULL,
+ REQUIRES_AUTHORIZATION BOOLEAN NOT NULL DEFAULT 'true',
+ CONSTRAINT IDENTIFIER_UNIQUE UNIQUE (IDENTIFIER, TENANT_ID)
+);
+
+DROP TABLE IF EXISTS SCOPE;
+DROP SEQUENCE IF EXISTS SCOPE_SEQ;
+CREATE SEQUENCE SCOPE_SEQ;
+CREATE TABLE SCOPE (
+ ID VARCHAR(255) NOT NULL PRIMARY KEY,
+ CURSOR_KEY SERIAL,
+ API_ID VARCHAR(255) NOT NULL,
+ NAME VARCHAR(255) NOT NULL,
+ DISPLAY_NAME VARCHAR(255) NOT NULL,
+ TENANT_ID INT NOT NULL,
+ DESCRIPTION VARCHAR(300),
+ FOREIGN KEY (API_ID) REFERENCES API_RESOURCE(ID) ON DELETE CASCADE,
+ CONSTRAINT SCOPE_UNIQUE UNIQUE (NAME, TENANT_ID)
+);
+
-- --------------------------- INDEX CREATION -----------------------------
-- IDN_OAUTH2_ACCESS_TOKEN --
CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED);
@@ -1643,3 +1673,6 @@ CREATE INDEX IDX_AUTH_PROP_AUTH_ID ON IDP_AUTHENTICATOR_PROPERTY (AUTHENTICATOR_
-- IDN_CONFIG_FILE --
CREATE INDEX IDX_CON_FILE_RES_ID ON IDN_CONFIG_FILE (RESOURCE_ID);
+
+-- SCOPE --
+CREATE INDEX API_ID_NAME_INDEX ON SCOPE (API_ID, NAME);
diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml.j2 b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml.j2
index 3b14b62aba00..758f03afd5de 100644
--- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml.j2
+++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml.j2
@@ -1011,6 +1011,15 @@
{% endif %}
+
+ {% if oauth.restricted_scopes is defined %}
+
+ {% for restricted_scope in oauth.restricted_scopes %}
+ {{restricted_scope}}
+ {% endfor %}
+
+ {% endif %}
+
@@ -3192,6 +3201,7 @@
/api/server/v1/admin-advisory-management/banner
/api/idle-account-identification/v1/inactive-users
/api/server/v1/expired-password-identification/password-expired-users
+ /api/server/v1/api-resources
diff --git a/pom.xml b/pom.xml
index 90dd379b9ff6..ece651df6672 100644
--- a/pom.xml
+++ b/pom.xml
@@ -101,6 +101,8 @@
features/central-logger
features/input-validation-mgt
features/consent-server-configs-mgt
+ components/api-resource-mgt
+ features/api-resource-mgt
@@ -638,6 +640,18 @@
${project.version}
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.api.resource.mgt
+ ${project.version}
+
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.api.resource.mgt.server.feature
+ ${project.version}
+
+
com.google.api-client
google-api-client