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