attributeDTOs = null;
+ if (StringUtils.isNotBlank(policy.getPolicy())) {
+ PolicyAttributeBuilder policyAttributeBuilder = new PolicyAttributeBuilder(policy.getPolicy());
+ attributeDTOs = policyAttributeBuilder.getAttributesFromPolicy();
+ }
+ if (attributeDTOs != null && !attributeDTOs.isEmpty()) {
+ policy.setAttributeDTOs(attributeDTOs.toArray(new AttributeDTO[0]));
+ }
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ policyDAO.insertPolicy(policy, tenantId);
+ }
+
+ /**
+ * Gets the requested policy.
+ *
+ * @param policyId policy ID.
+ * @return policyDTO object.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public PolicyDTO getPAPPolicy(String policyId) throws EntitlementException {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Retrieving entitlement policy %s", policyId));
+ }
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+
+ return policyDAO.getPAPPolicy(policyId, tenantId);
+ }
+
+ /**
+ * Gets the requested policy list.
+ *
+ * Note: The `policyIds` parameter is ignored. This method retrieves the full list of PAP policies from the database
+ * regardless of the provided policy IDs.
+ *
+ *
+ * @param policyIds A list of policy IDs. This parameter is ignored.
+ * @return policyDTO.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public List getPAPPolicies(List policyIds) throws EntitlementException {
+
+ LOG.debug("Retrieving all PAP entitlement policies");
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ return policyDAO.getAllPAPPolicies(tenantId);
+ }
+
+ /**
+ * Gets the requested policy version. Returns the latest version if version is not specified.
+ *
+ * @param policyId policy ID.
+ * @param version policy version.
+ * @return policyDTO object.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public PolicyDTO getPolicy(String policyId, String version) throws EntitlementException {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+
+ // Zero means current version
+ if (StringUtils.isBlank(version)) {
+ version = policyDAO.getLatestPolicyVersion(policyId, tenantId);
+ if (StringUtils.isBlank(version)) {
+ throw new EntitlementException("Invalid policy version");
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Retrieving entitlement policy %s for the given version %s", policyId, version));
+ }
+ return policyDAO.getPapPolicyByVersion(policyId, version, tenantId);
+ }
+
+ /**
+ * Gets all versions of the given policy ID.
+ *
+ * @param policyId policy ID.
+ * @return array of policy versions.
+ */
+ @Override
+ public String[] getVersions(String policyId) {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ List versions = policyDAO.getPolicyVersions(policyId, tenantId);
+ return versions.toArray(new String[0]);
+ }
+
+ /**
+ * Lists all PAP policy IDs.
+ *
+ * @return list of policy IDs.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public List listPolicyIds() throws EntitlementException {
+
+ LOG.debug("Retrieving all entitlement policy IDs");
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ return policyDAO.getPAPPolicyIds(tenantId);
+ }
+
+ /**
+ * Removes the given policy from PAP.
+ *
+ * @param policyId policy ID.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public void removePolicy(String policyId) throws EntitlementException {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Removing entitlement policy %s", policyId));
+ }
+ if (StringUtils.isBlank(policyId)) {
+ throw new EntitlementException("Invalid policy id. Policy id can not be null");
+ }
+ policyDAO.deletePAPPolicy(policyId, tenantId);
+ }
+
+ /**
+ * Gets the name of the module.
+ *
+ * @return name as String.
+ */
+ @Override
+ public String getModuleName() {
+
+ return MODULE_NAME;
+ }
+
+ /**
+ * Gets the published policy for the given policy ID.
+ *
+ * @param policyId policy id as a string value.
+ * @return policy as string.
+ */
+ @Override
+ public String getPolicy(String policyId) {
+
+ PolicyStoreDTO dto = getPublishedPolicy(policyId);
+ return dto.getPolicy();
+ }
+
+ /**
+ * Gets the policy order.
+ *
+ * @param policyId policy id as a string value.
+ * @return policy order.
+ */
+ @Override
+ public int getPolicyOrder(String policyId) {
+
+ PolicyStoreDTO dto = getPublishedPolicy(policyId);
+ return dto.getPolicyOrder();
+ }
+
+ /**
+ * Gets all supported active, published policies.
+ * If policy ordering is supported by the module itself, these policies must be ordered.
+ *
+ * @return array of policies as Strings.
+ */
+ @Override
+ public String[] getActivePolicies() {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Retrieving of Active policies has started at %s", new Date()));
+ }
+
+ List policies = new ArrayList<>();
+
+ try {
+ PolicyStoreDTO[] policyDTOs = getAllPolicies(true, true);
+ for (PolicyStoreDTO dto : policyDTOs) {
+ if (StringUtils.isNotBlank(dto.getPolicy())) {
+ policies.add(dto.getPolicy());
+ }
+ }
+ } catch (EntitlementException e) {
+ LOG.error(ERROR_RETRIEVING_POLICIES_FROM_POLICY_FINDER, e);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Retrieving of Active policies has finished at %s", new Date()));
+ }
+
+ return policies.toArray(new String[0]);
+ }
+
+ /**
+ * Gets all supported ordered policy ids.
+ * If policy ordering is supported by the module itself, these policy ids must be ordered.
+ *
+ * @return array of policy ids as Strings.
+ */
+ @Override
+ public String[] getOrderedPolicyIdentifiers() {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Retrieving of Ordered Policy Ids has started at %s", new Date()));
+ }
+
+ List policies = new ArrayList<>();
+
+ try {
+ PolicyStoreDTO[] policyDTOs = getAllPolicies(false, true);
+ for (PolicyStoreDTO dto : policyDTOs) {
+ if (StringUtils.isNotBlank(dto.getPolicy())) {
+ policies.add(dto.getPolicyId());
+ }
+ }
+ } catch (EntitlementException e) {
+ LOG.error(ERROR_RETRIEVING_POLICIES_FROM_POLICY_FINDER, e);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Retrieving of Ordered Policy Ids is finished at %s", new Date()));
+ }
+
+ return policies.toArray(new String[0]);
+ }
+
+ /**
+ * Gets all published policy ids.
+ *
+ * @return array of policy ids as Strings.
+ */
+ @Override
+ public String[] getPolicyIdentifiers() {
+
+ String[] policyIds = null;
+ try {
+ policyIds = listPublishedPolicyIds().toArray(new String[0]);
+ } catch (EntitlementException e) {
+ LOG.error("Policy identifiers can not be retrieved from the policy finder module", e);
+ }
+ return policyIds;
+ }
+
+ /**
+ * Gets reference policy for the given policy ID.
+ * Reference policy can not be with PDP policy store, may be in some external policy store.
+ * Therefore, a new method has been added to retrieve reference policies.
+ *
+ * @param policyId policy id as String value.
+ * @return reference policy as String.
+ */
+ @Override
+ public String getReferencedPolicy(String policyId) {
+
+ // Retrieve policies that are not active
+ PolicyStoreDTO dto = getPublishedPolicy(policyId);
+ if (dto != null && StringUtils.isNotBlank(dto.getPolicy()) && !dto.isActive()) {
+ return dto.getPolicy();
+ }
+ return null;
+ }
+
+ /**
+ * Gets attributes that are used for policy searching.
+ *
+ * @param identifier unique identifier to separate out search attributes.
+ * @param givenAttribute pre-given attributes to retrieve other attributes.
+ * @return return search attributes based on a given policy, Map of policy id with search attributes.
+ */
+ @Override
+ public Map> getSearchAttributes(String identifier, Set givenAttribute) {
+
+ try {
+ PolicyStoreDTO[] policyDTOs = getAllPolicies(true, true);
+ List policyDTOList = new ArrayList<>();
+ for (PolicyStoreDTO policyStoreDTO : policyDTOs) {
+ PolicyDTO policyDTO = getPAPPolicy(policyStoreDTO.getPolicyId());
+ policyDTOList.add(policyDTO);
+ }
+ if (policyDTOs.length > 0) {
+ return EntitlementUtil.getAttributesFromPolicies(policyDTOList.toArray(new PolicyDTO[0]));
+ }
+ } catch (EntitlementException e) {
+ LOG.error(ERROR_RETRIEVING_POLICIES_FROM_POLICY_FINDER, e);
+ }
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Gets support attribute searching scheme of the module.
+ *
+ * @return return scheme identifier value.
+ */
+ @Override
+ public int getSupportedSearchAttributesScheme() {
+
+ return PolicyFinderModule.COMBINATIONS_BY_CATEGORY_AND_PARAMETER;
+ }
+
+ /**
+ * Publishes the given policy.
+ *
+ * @param policy policy to be published.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public void addPolicy(PolicyStoreDTO policy) throws EntitlementException {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+
+ if (policy == null || StringUtils.isBlank(policy.getPolicyId())) {
+ throw new EntitlementException("Policy and policy id can not be null");
+ }
+ if (StringUtils.isBlank(policy.getVersion())) {
+ throw new EntitlementException(String.format("Cannot publish policy %s. Invalid policy version.",
+ policy.getPolicyId()));
+ }
+ policyDAO.insertOrUpdatePolicy(policy, tenantId);
+ }
+
+ /**
+ * Updates the policy.
+ *
+ * @param policy policy.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public void updatePolicy(PolicyStoreDTO policy) throws EntitlementException {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Updating policy %s", policy.getPolicyId()));
+ }
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+
+ if (policy == null || StringUtils.isBlank(policy.getPolicyId())) {
+ throw new EntitlementException("Policy and policy id can not be null");
+ }
+ if (policy.isSetActive() != policy.isSetOrder()) {
+ if (StringUtils.isBlank(policy.getVersion())) {
+ // Get published version
+ int version = policyDAO.getPublishedVersion(policy, tenantId);
+ if (version == -1) {
+ throw new EntitlementException(String.format("Cannot update policy %s. Invalid policy version.",
+ policy.getPolicyId()));
+ }
+ policy.setVersion(String.valueOf(version));
+ }
+ policyDAO.updateActiveStatusAndOrder(policy, tenantId);
+ } else {
+ addPolicy(policy);
+ }
+ }
+
+ /**
+ * Checks whether the given policy is published or not.
+ *
+ * @param policyId policy ID.
+ * @return whether the given policy is published or not.
+ */
+ @Override
+ public boolean isPolicyExist(String policyId) {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ if (StringUtils.isBlank(policyId)) {
+ return false;
+ }
+ return policyDAO.isPolicyPublished(policyId, tenantId);
+ }
+
+ /**
+ * Gets the requested published policy.
+ *
+ * @param policyId policy ID.
+ * @return requested policy.
+ */
+ @Override
+ public PolicyStoreDTO getPublishedPolicy(String policyId) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Retrieving entitlement policy %s", policyId));
+ }
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+
+ PolicyStoreDTO dto = policyDAO.getPDPPolicy(policyId, tenantId);
+ if (dto != null) {
+ return dto;
+ }
+ return new PolicyStoreDTO();
+ }
+
+ /**
+ * Lists all published policy IDs.
+ *
+ * @return list of published policy IDs.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public List listPublishedPolicyIds() throws EntitlementException {
+
+ LOG.debug("Retrieving all PDP entitlement policy ids");
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ return policyDAO.getPublishedPolicyIds(tenantId);
+ }
+
+ /**
+ * Un-publishes the policy.
+ *
+ * @param policyId policy ID.
+ */
+ @Override
+ public boolean deletePolicy(String policyId) {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+
+ if (StringUtils.isBlank(policyId)) {
+ return false;
+ }
+ return policyDAO.unpublishPolicy(policyId, tenantId);
+ }
+
+ /**
+ * Checks the existence of the policy in PAP
+ *
+ * @param policyId policy ID.
+ * @return whether the policy exists in PAP or not.
+ */
+ public boolean isPolicyExistsInPap(String policyId) {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+
+ if (policyId == null || policyId.trim().isEmpty()) {
+ return false;
+ }
+ return policyDAO.isPAPPolicyExists(policyId, tenantId);
+ }
+
+ /**
+ * Creates policy versions.
+ *
+ * @param policyDTO policyDTO.
+ * @return version.
+ * @throws EntitlementException throws, if fails.
+ */
+ private String createVersion(PolicyDTO policyDTO) throws EntitlementException {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ PAPPolicyStoreManager manager = new PAPPolicyStoreManager();
+ String version = "0";
+
+ if (manager.isExistPolicy(policyDTO.getPolicyId())) {
+ PolicyDTO dto = manager.getLightPolicy(policyDTO.getPolicyId());
+ version = dto.getVersion();
+ }
+
+ int versionInt = Integer.parseInt(version);
+
+ // Check whether this is larger than max version
+ if (versionInt > maxVersions) {
+ // delete the older version
+ int olderVersion = versionInt - maxVersions;
+ policyDAO.deletePAPPolicyVersion(policyDTO.getPolicyId(), olderVersion, tenantId);
+ }
+
+ // New version
+ version = Integer.toString(versionInt + 1);
+ return version;
+ }
+
+ /**
+ * Reads all ordered and active policies as PolicyDTO.
+ *
+ * @param active only return active policies. Else return all policies.
+ * @param order return ordered policy.
+ * @return Array of PolicyDTO.
+ * @throws EntitlementException If an error occurs.
+ */
+ private PolicyStoreDTO[] getAllPolicies(boolean active, boolean order) throws EntitlementException {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ PolicyStoreDTO[] policies;
+ policies = policyDAO.getAllPDPPolicies(tenantId);
+
+ if (policies.length == 0) {
+ return new PolicyStoreDTO[0];
+ }
+ List policyDTOList = new ArrayList<>();
+ for (PolicyStoreDTO policy : policies) {
+ if (active) {
+ if (policy.isActive()) {
+ policyDTOList.add(policy);
+ }
+ } else {
+ policyDTOList.add(policy);
+ }
+ }
+
+ PolicyStoreDTO[] policyDTOs = policyDTOList.toArray(new PolicyStoreDTO[0]);
+
+ if (order) {
+ Arrays.sort(policyDTOs, new PolicyOrderComparator());
+ }
+ return policyDTOs;
+ }
+}
diff --git a/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/JDBCSimplePAPStatusDataHandler.java b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/JDBCSimplePAPStatusDataHandler.java
new file mode 100644
index 000000000000..05fd859f14de
--- /dev/null
+++ b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/JDBCSimplePAPStatusDataHandler.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * 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.entitlement.persistence;
+
+import org.apache.commons.logging.Log;
+import org.wso2.carbon.CarbonConstants;
+import org.wso2.carbon.context.CarbonContext;
+import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils;
+import org.wso2.carbon.identity.core.util.IdentityUtil;
+import org.wso2.carbon.identity.entitlement.EntitlementException;
+import org.wso2.carbon.identity.entitlement.EntitlementUtil;
+import org.wso2.carbon.identity.entitlement.PAPStatusDataHandler;
+import org.wso2.carbon.identity.entitlement.common.EntitlementConstants;
+import org.wso2.carbon.identity.entitlement.dto.StatusHolder;
+import org.wso2.carbon.identity.entitlement.persistence.dao.StatusDAO;
+
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * This class handles the status data of the policies in the JDBC data store.
+ */
+public class JDBCSimplePAPStatusDataHandler implements PAPStatusDataHandler {
+
+ private static final Log AUDIT_LOG = CarbonConstants.AUDIT_LOG;
+ private static final String AUDIT_MESSAGE
+ = "Initiator : %s | Action : %s | Target : %s | Data : { %s } | Result : %s ";
+ private int maxRecords;
+ private static final StatusDAO statusDAO = new StatusDAO();
+
+ /**
+ * init entitlement status data handler module.
+ *
+ * @param properties properties.
+ */
+ @Override
+ public void init(Properties properties) {
+
+ maxRecords = EntitlementUtil.getMaxNoOfStatusRecords();
+ }
+
+ /**
+ * Handles the status data.
+ *
+ * @param about whether the status is about a policy or publisher.
+ * @param key key value of the status.
+ * @param statusHolders StatusHolder
.
+ * @throws EntitlementException throws, if fails to handle.
+ */
+ @Override
+ public void handle(String about, String key, List statusHolders) throws EntitlementException {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ // If the action is DELETE_POLICY, delete the policy or the subscriber status
+ for (StatusHolder holder : statusHolders) {
+ if (EntitlementConstants.StatusTypes.DELETE_POLICY.equals(holder.getType())) {
+ statusDAO.deleteStatusTrail(about, key, tenantId);
+ return;
+ }
+ }
+ amendStatusTrail(about, key, statusHolders, tenantId);
+ }
+
+ /**
+ * Returns status data.
+ *
+ * @param about indicates what is related with this admin status action.
+ * @param key key value of the status.
+ * @param type admin action type.
+ * @param searchString search string for StatusHolder
.
+ * @return An array of StatusHolder
.
+ * @throws EntitlementException if fails.
+ */
+ @Override
+ public StatusHolder[] getStatusData(String about, String key, String type, String searchString)
+ throws EntitlementException {
+
+ String statusAboutType = EntitlementConstants.Status.ABOUT_POLICY.equals(about)
+ ? EntitlementConstants.Status.ABOUT_POLICY
+ : EntitlementConstants.Status.ABOUT_SUBSCRIBER;
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ List holders = statusDAO.getStatus(key, statusAboutType, tenantId);
+ return EntitlementUtil.filterStatus(holders, searchString, about, type);
+ }
+
+ private void amendStatusTrail(String about, String key, List statusHolders, int tenantId)
+ throws EntitlementException {
+
+ boolean useLastStatusOnly = Boolean.parseBoolean(
+ IdentityUtil.getProperty(EntitlementConstants.PROP_USE_LAST_STATUS_ONLY));
+
+ if (statusHolders != null && !statusHolders.isEmpty()) {
+
+ if (useLastStatusOnly) {
+ // Delete all the previous statuses
+ statusDAO.deleteStatusTrail(about, key, tenantId);
+ auditAction(statusHolders.toArray(new StatusHolder[0]));
+ }
+
+ // Add new status to the database
+ statusDAO.insertStatus(about, key, statusHolders, tenantId);
+
+ if (!useLastStatusOnly) {
+ statusDAO.deleteExcessStatusData(about, key, tenantId, maxRecords);
+ }
+ }
+ }
+
+ private void auditAction(StatusHolder[] statusHolders) {
+
+ if (statusHolders != null) {
+ for (StatusHolder statusHolder : statusHolders) {
+ if (statusHolder != null) {
+ String initiator = statusHolder.getUser();
+ if (LoggerUtils.isLogMaskingEnable) {
+ initiator = LoggerUtils.getMaskedContent(initiator);
+ }
+ String action = statusHolder.getType();
+ String key = statusHolder.getKey();
+ String target = statusHolder.getTarget();
+ String targetAction = statusHolder.getTargetAction();
+ String result = "FAILURE";
+ if (statusHolder.isSuccess()) {
+ result = "SUCCESS";
+ }
+ String auditData = String.format("\"Key\" : \"%s\" , \"Target Action\" : \"%s\"",
+ key, targetAction);
+
+ AUDIT_LOG.info(String.format(AUDIT_MESSAGE, initiator, action, target, auditData, result));
+ }
+ }
+ }
+ }
+}
diff --git a/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/JDBCSubscriberPersistenceManager.java b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/JDBCSubscriberPersistenceManager.java
new file mode 100644
index 000000000000..fe1b72e15c13
--- /dev/null
+++ b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/JDBCSubscriberPersistenceManager.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * 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.entitlement.persistence;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.context.CarbonContext;
+import org.wso2.carbon.core.util.CryptoException;
+import org.wso2.carbon.core.util.CryptoUtil;
+import org.wso2.carbon.identity.entitlement.EntitlementException;
+import org.wso2.carbon.identity.entitlement.EntitlementUtil;
+import org.wso2.carbon.identity.entitlement.common.EntitlementConstants;
+import org.wso2.carbon.identity.entitlement.dto.PublisherDataHolder;
+import org.wso2.carbon.identity.entitlement.dto.PublisherPropertyDTO;
+import org.wso2.carbon.identity.entitlement.persistence.cache.CacheBackedSubscriberDAO;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class handles the JDBC operations of the subscribers in the data store.
+ */
+public class JDBCSubscriberPersistenceManager implements SubscriberPersistenceManager {
+
+ private static final Log LOG = LogFactory.getLog(JDBCSubscriberPersistenceManager.class);
+ private static final String ERROR_SUBSCRIBER_ID_NULL = "Subscriber Id can not be null";
+ private static final CacheBackedSubscriberDAO subscriberDAO = CacheBackedSubscriberDAO.getInstance();
+
+ /**
+ * Gets the requested subscriber.
+ *
+ * @param subscriberId subscriber ID.
+ * @param shouldDecryptSecrets whether the subscriber should get returned with secret(decrypted) values or not.
+ * @return publisher data holder.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public PublisherDataHolder getSubscriber(String subscriberId, boolean shouldDecryptSecrets)
+ throws EntitlementException {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+
+ PublisherDataHolder publisherDataHolder = subscriberDAO.getSubscriber(subscriberId, tenantId);
+ if (publisherDataHolder == null) {
+ return null;
+ }
+ if (shouldDecryptSecrets) {
+ decryptSecretProperties(publisherDataHolder.getPropertyDTOs());
+ }
+ return publisherDataHolder;
+ }
+
+ /**
+ * Gets all subscriber IDs.
+ *
+ * @param filter search string.
+ * @return list of subscriber IDs.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public List listSubscriberIds(String filter) throws EntitlementException {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ List subscriberIdList = subscriberDAO.getSubscriberIds(tenantId);
+ return EntitlementUtil.filterSubscribers(subscriberIdList, filter);
+ }
+
+ /**
+ * Adds a subscriber.
+ *
+ * @param holder publisher data holder.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public void addSubscriber(PublisherDataHolder holder) throws EntitlementException {
+
+ String subscriberId = EntitlementUtil.resolveSubscriberId(holder);
+ if (subscriberId == null) {
+ throw new EntitlementException(ERROR_SUBSCRIBER_ID_NULL);
+ }
+
+ if (isSubscriberExists(subscriberId)) {
+ throw new EntitlementException("Subscriber ID already exists");
+ }
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ subscriberDAO.insertSubscriber(subscriberId, holder, tenantId);
+ }
+
+ /**
+ * Updates a subscriber.
+ *
+ * @param holder publisher data holder.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public void updateSubscriber(PublisherDataHolder holder) throws EntitlementException {
+
+ String subscriberId = EntitlementUtil.resolveSubscriberId(holder);
+ if (subscriberId == null) {
+ throw new EntitlementException(ERROR_SUBSCRIBER_ID_NULL);
+ }
+
+ if (isSubscriberExists(subscriberId)) {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ PublisherDataHolder oldHolder = getSubscriber(subscriberId, false);
+ String updatedModuleName = getUpdatedModuleName(holder, oldHolder);
+ PublisherPropertyDTO[] updatedPropertyDTOs = getUpdatedPropertyDTOs(holder, oldHolder);
+ updatedPropertyDTOs = encryptUpdatedSecretProperties(updatedPropertyDTOs);
+ subscriberDAO.updateSubscriber(subscriberId, updatedModuleName, updatedPropertyDTOs, tenantId);
+ } else {
+ throw new EntitlementException("Subscriber ID does not exist; update cannot be done");
+ }
+ }
+
+ /**
+ * Removes the subscriber of the given subscriber ID.
+ *
+ * @param subscriberId subscriber ID.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public void removeSubscriber(String subscriberId) throws EntitlementException {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+
+ if (StringUtils.isBlank(subscriberId)) {
+ throw new EntitlementException(ERROR_SUBSCRIBER_ID_NULL);
+ }
+
+ if (EntitlementConstants.PDP_SUBSCRIBER_ID.equals(subscriberId.trim())) {
+ throw new EntitlementException("Cannot delete PDP publisher");
+ }
+
+ subscriberDAO.deleteSubscriber(subscriberId, tenantId);
+ }
+
+ /**
+ * Checks whether a subscriber exists.
+ *
+ * @param subscriberId subscriber ID.
+ * @return whether the subscriber exists or not.
+ * @throws EntitlementException If an error occurs.
+ */
+ public boolean isSubscriberExists(String subscriberId) throws EntitlementException {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ return subscriberDAO.isSubscriberExists(subscriberId, tenantId);
+ }
+
+ private String getUpdatedModuleName(PublisherDataHolder holder, PublisherDataHolder oldHolder) {
+
+ if (holder == null || oldHolder == null) {
+ return null;
+ }
+ if (!oldHolder.getModuleName().equalsIgnoreCase(holder.getModuleName())) {
+ return holder.getModuleName();
+ }
+ return null;
+ }
+
+ private PublisherPropertyDTO[] getUpdatedPropertyDTOs(PublisherDataHolder holder, PublisherDataHolder oldHolder) {
+
+ if (holder == null || oldHolder == null) {
+ return new PublisherPropertyDTO[0];
+ }
+ List updatedPropertyDTOs = new ArrayList<>();
+ for (PublisherPropertyDTO newPropertyDTO : holder.getPropertyDTOs()) {
+ if (StringUtils.isNotBlank(newPropertyDTO.getId()) && StringUtils.isNotBlank(newPropertyDTO.getValue())) {
+
+ PublisherPropertyDTO oldPropertyDTO = oldHolder.getPropertyDTO(newPropertyDTO.getId());
+ if (oldPropertyDTO == null || !oldPropertyDTO.getValue().equalsIgnoreCase(newPropertyDTO.getValue())) {
+ updatedPropertyDTOs.add(newPropertyDTO);
+ }
+ }
+ }
+ return updatedPropertyDTOs.toArray(new PublisherPropertyDTO[0]);
+ }
+
+ /**
+ * Sets the base64 encoded secret value of the secret subscriber properties, if it has been updated.
+ *
+ * @param propertyDTOs list of subscriber properties
+ */
+ private PublisherPropertyDTO[] encryptUpdatedSecretProperties(PublisherPropertyDTO[] propertyDTOs)
+ throws EntitlementException {
+
+ if (propertyDTOs == null) {
+ return new PublisherPropertyDTO[0];
+ }
+ List updatedPropertyDTOs = new ArrayList<>();
+ for (PublisherPropertyDTO propertyDTO : propertyDTOs) {
+ if (propertyDTO.isSecret()) {
+ try {
+ String encryptedValue = CryptoUtil.getDefaultCryptoUtil()
+ .encryptAndBase64Encode(propertyDTO.getValue().getBytes());
+ propertyDTO.setValue(encryptedValue);
+ } catch (CryptoException e) {
+ throw new EntitlementException("Error while encrypting secret value of subscriber. Update cannot " +
+ "proceed.", e);
+ }
+ }
+ updatedPropertyDTOs.add(propertyDTO);
+ }
+ return updatedPropertyDTOs.toArray(new PublisherPropertyDTO[0]);
+ }
+
+ /**
+ * Decrypts the secret values of the subscriber properties.
+ *
+ * @param properties list of subscriber properties
+ */
+ // TODO: check if we can use common secret table or a separate table
+ private void decryptSecretProperties(PublisherPropertyDTO[] properties) {
+
+ for (PublisherPropertyDTO dto : properties) {
+ if (dto.isSecret()) {
+ try {
+ String password = new String(CryptoUtil.getDefaultCryptoUtil()
+ .base64DecodeAndDecrypt(dto.getValue()));
+ dto.setValue(password);
+ } catch (CryptoException e) {
+ LOG.error("Error while decrypting secret value of subscriber.", e);
+ }
+ }
+ }
+ }
+}
diff --git a/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/PersistenceManagerConstants.java b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/PersistenceManagerConstants.java
new file mode 100644
index 000000000000..2302c2b09fbf
--- /dev/null
+++ b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/PersistenceManagerConstants.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * 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.entitlement.persistence;
+
+/**
+ * DB related constant values.
+ */
+public class PersistenceManagerConstants {
+
+ private PersistenceManagerConstants() {
+
+ }
+
+ public static final String LIMIT = "LIMIT";
+ public static final String KEY = "KEY";
+ public static final String STATUS_COUNT = "COUNT";
+
+ public static class EntitlementTableColumns {
+
+ private EntitlementTableColumns() {
+
+ }
+
+ // IDN_XACML_POLICY table
+ public static final String POLICY_ID = "POLICY_ID";
+ public static final String VERSION = "VERSION";
+ public static final String TENANT_ID = "TENANT_ID";
+ public static final String LAST_MODIFIED_TIME = "LAST_MODIFIED_TIME";
+ public static final String LAST_MODIFIED_USER = "LAST_MODIFIED_USER";
+ public static final String IS_ACTIVE = "IS_ACTIVE";
+ public static final String POLICY_ORDER = "POLICY_ORDER";
+ public static final String POLICY_TYPE = "POLICY_TYPE";
+ public static final String POLICY_EDITOR = "POLICY_EDITOR";
+ public static final String POLICY = "POLICY";
+ public static final String IS_IN_PAP = "IS_IN_PAP";
+ public static final String IS_IN_PDP = "IS_IN_PDP";
+
+ // IDN_XACML_POLICY_EDITOR_DATA table
+ public static final String EDITOR_DATA_ORDER = "DATA_ORDER";
+ public static final String EDITOR_DATA = "DATA";
+
+ // IDN_XACML_POLICY_ATTRIBUTE table
+ public static final String ATTRIBUTE_ID = "ATTRIBUTE_ID";
+ public static final String ATTRIBUTE_VALUE = "ATTRIBUTE_VALUE";
+ public static final String DATA_TYPE = "DATA_TYPE";
+ public static final String CATEGORY = "CATEGORY";
+
+ // IDN_XACML_POLICY_REFERENCE table
+ public static final String REFERENCE = "REFERENCE";
+
+ // IDN_XACML_POLICY_SET_REFERENCE table
+ public static final String SET_REFERENCE = "SET_REFERENCE";
+
+ // IDN_XACML_SUBSCRIBER table
+ public static final String SUBSCRIBER_ID = "SUBSCRIBER_ID";
+ public static final String ENTITLEMENT_MODULE_NAME = "ENTITLEMENT_MODULE_NAME";
+
+ // IDN_XACML_SUBSCRIBER_PROPERTY table
+ public static final String PROPERTY_ID = "PROPERTY_ID";
+ public static final String DISPLAY_NAME = "DISPLAY_NAME";
+ public static final String IS_REQUIRED = "IS_REQUIRED";
+ public static final String DISPLAY_ORDER = "DISPLAY_ORDER";
+ public static final String IS_SECRET = "IS_SECRET";
+ public static final String MODULE = "PROPERTY_MODULE";
+ public static final String PROPERTY_VALUE = "PROPERTY_VALUE";
+
+ // IDN_XACML_POLICY_STATUS and IDN_XACML_SUBSCRIBER_STATUS tables
+ public static final String STATUS_TYPE = "TYPE";
+ public static final String IS_SUCCESS = "IS_SUCCESS";
+ public static final String USER = "USERNAME";
+ public static final String TARGET = "TARGET";
+ public static final String TARGET_ACTION = "TARGET_ACTION";
+ public static final String LOGGED_AT = "LOGGED_AT";
+ public static final String MESSAGE = "MESSAGE";
+ public static final String POLICY_VERSION = "POLICY_VERSION";
+
+ // IDN_XACML_CONFIG table
+ public static final String CONFIG_KEY = "CONFIG_KEY";
+ public static final String CONFIG_VALUE = "CONFIG_VALUE";
+ }
+
+ public static class DatabaseTypes {
+
+ private DatabaseTypes() {
+
+ }
+
+ public static final String MYSQL = "MySQL";
+ public static final String MSSQL = "Microsoft SQL Server";
+ public static final String ORACLE = "ORACLE";
+ public static final String MARIADB = "MariaDB";
+ public static final String DB2 = "DB2";
+ public static final String H2 = "H2";
+ public static final String POSTGRES = "PostgreSQL";
+ }
+
+ /**
+ * SQL queries for XACML policy storage and management.
+ */
+ public static class SQLQueries {
+
+ private SQLQueries() {
+
+ }
+
+ // TODO: revisit all queries using constants like, IN_PAP, IN_PDP, INACTIVE and check if they can be embedded
+ /**
+ * DB queries related to PAP policy store.
+ */
+ public static final String CREATE_PAP_POLICY_SQL = "INSERT INTO IDN_XACML_POLICY (POLICY_ID, VERSION, " +
+ " IS_IN_PDP, IS_IN_PAP, POLICY, IS_ACTIVE, POLICY_TYPE, POLICY_EDITOR, POLICY_ORDER, " +
+ "LAST_MODIFIED_TIME, LAST_MODIFIED_USER, TENANT_ID) VALUES (:POLICY_ID;, :VERSION;, :IS_IN_PDP;, " +
+ ":IS_IN_PAP;, :POLICY;, :IS_ACTIVE;, :POLICY_TYPE;, :POLICY_EDITOR;, :POLICY_ORDER;, " +
+ ":LAST_MODIFIED_TIME;, :LAST_MODIFIED_USER;, :TENANT_ID;)";
+ public static final String CREATE_PAP_POLICY_REFS_SQL = "INSERT INTO IDN_XACML_POLICY_REFERENCE " +
+ "(REFERENCE, POLICY_ID, VERSION, TENANT_ID) VALUES (:REFERENCE;, :POLICY_ID;, :VERSION;, :TENANT_ID;)";
+ public static final String CREATE_PAP_POLICY_SET_REFS_SQL = "INSERT INTO IDN_XACML_POLICY_SET_REFERENCE " +
+ "(SET_REFERENCE, POLICY_ID, VERSION, TENANT_ID) VALUES (:SET_REFERENCE;, :POLICY_ID;, :VERSION;, " +
+ ":TENANT_ID;)";
+ public static final String CREATE_PAP_POLICY_ATTRIBUTES_SQL = "INSERT INTO IDN_XACML_POLICY_ATTRIBUTE " +
+ "(ATTRIBUTE_ID, ATTRIBUTE_VALUE, DATA_TYPE, CATEGORY, POLICY_ID, VERSION, TENANT_ID) VALUES " +
+ "(:ATTRIBUTE_ID;, :ATTRIBUTE_VALUE;, :DATA_TYPE;, :CATEGORY;, :POLICY_ID;, :VERSION;, :TENANT_ID;)";
+ public static final String CREATE_PAP_POLICY_EDITOR_DATA_SQL = "INSERT INTO IDN_XACML_POLICY_EDITOR_DATA " +
+ "(DATA_ORDER, DATA, POLICY_ID, VERSION, TENANT_ID) VALUES (:DATA_ORDER;, :DATA;, :POLICY_ID;, " +
+ ":VERSION;, :TENANT_ID;)";
+ public static final String GET_PAP_POLICY_IDS_SQL = "SELECT DISTINCT POLICY_ID FROM IDN_XACML_POLICY WHERE " +
+ "IS_IN_PAP= :IS_IN_PAP; AND TENANT_ID= :TENANT_ID;";
+ public static final String GET_PAP_POLICY_SQL =
+ "SELECT POLICY_ID, VERSION, LAST_MODIFIED_TIME, LAST_MODIFIED_USER, IS_ACTIVE, POLICY_ORDER, " +
+ "POLICY_TYPE, POLICY_EDITOR, POLICY, TENANT_ID FROM IDN_XACML_POLICY WHERE " +
+ "IS_IN_PAP = :IS_IN_PAP; AND POLICY_ID = :POLICY_ID; AND VERSION = (SELECT MAX(VERSION) " +
+ "FROM IDN_XACML_POLICY WHERE POLICY_ID = :POLICY_ID; AND TENANT_ID= :TENANT_ID;) " +
+ "AND TENANT_ID = :TENANT_ID;";
+ public static final String GET_PAP_POLICY_REFS_SQL = "SELECT REFERENCE FROM IDN_XACML_POLICY_REFERENCE " +
+ "WHERE POLICY_ID=:POLICY_ID; AND VERSION=:VERSION; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_PAP_POLICY_SET_REFS_SQL =
+ "SELECT SET_REFERENCE FROM IDN_XACML_POLICY_SET_REFERENCE WHERE " +
+ "POLICY_ID=:POLICY_ID; AND VERSION=:VERSION; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_PAP_POLICY_EDITOR_DATA_SQL =
+ "SELECT DATA_ORDER, DATA FROM IDN_XACML_POLICY_EDITOR_DATA WHERE POLICY_ID=:POLICY_ID; AND " +
+ "VERSION=:VERSION; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_PAP_POLICY_META_DATA_SQL = "SELECT ATTRIBUTE_ID, ATTRIBUTE_VALUE, DATA_TYPE, " +
+ "CATEGORY FROM IDN_XACML_POLICY_ATTRIBUTE WHERE POLICY_ID=:POLICY_ID; AND VERSION=:VERSION; " +
+ "AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_PAP_POLICY_BY_VERSION_SQL = "SELECT POLICY_ID, LAST_MODIFIED_TIME, " +
+ "LAST_MODIFIED_USER, IS_ACTIVE, POLICY_ORDER, POLICY_TYPE, POLICY_EDITOR, POLICY, VERSION, TENANT_ID " +
+ "FROM IDN_XACML_POLICY WHERE IS_IN_PAP = :IS_IN_PAP; AND POLICY_ID = :POLICY_ID; AND " +
+ "VERSION = :VERSION; AND TENANT_ID = :TENANT_ID;";
+ public static final String GET_ALL_PAP_POLICIES_SQL = "SELECT t1.POLICY_ID, t1.VERSION, t1" +
+ ".LAST_MODIFIED_TIME, t1.LAST_MODIFIED_USER, t1.IS_ACTIVE, t1.POLICY_ORDER, t1.POLICY_TYPE, " +
+ "t1.POLICY_EDITOR, t1.POLICY, t1.TENANT_ID FROM IDN_XACML_POLICY t1 WHERE t1.IS_IN_PAP = :IS_IN_PAP; " +
+ "AND t1.VERSION =(SELECT MAX(VERSION) FROM IDN_XACML_POLICY t2 WHERE " +
+ "t2.POLICY_ID = t1.POLICY_ID AND t2.TENANT_ID = :TENANT_ID;) AND t1.TENANT_ID = :TENANT_ID;";
+ public static final String DELETE_PAP_POLICY_SQL = "UPDATE IDN_XACML_POLICY SET IS_IN_PAP=:IS_IN_PAP; " +
+ "WHERE IS_IN_PDP=:IS_IN_PDP; AND POLICY_ID=:POLICY_ID; AND TENANT_ID=:TENANT_ID;";
+ public static final String DELETE_PAP_POLICY_BY_VERSION_SQL =
+ "UPDATE IDN_XACML_POLICY SET IS_IN_PAP=:IS_IN_PAP; " +
+ "WHERE POLICY_ID=:POLICY_ID; AND VERSION=:VERSION; AND TENANT_ID=:TENANT_ID;";
+ public static final String DELETE_UNPUBLISHED_POLICY_VERSIONS_SQL = "DELETE FROM IDN_XACML_POLICY " +
+ "WHERE IS_IN_PDP=:IS_IN_PDP; AND POLICY_ID=:POLICY_ID; AND TENANT_ID=:TENANT_ID;";
+ public static final String DELETE_POLICY_SQL =
+ "DELETE FROM IDN_XACML_POLICY WHERE POLICY_ID=:POLICY_ID; AND TENANT_ID=:TENANT_ID;";
+ public static final String DELETE_POLICY_VERSION_SQL =
+ "DELETE FROM IDN_XACML_POLICY WHERE POLICY_ID=:POLICY_ID; " +
+ "AND VERSION=:VERSION; AND TENANT_ID=:TENANT_ID;";
+
+ /**
+ * DB queries related to PDP policy store.
+ */
+ public static final String CREATE_POLICY_COMBINING_ALGORITHM_SQL = "INSERT INTO IDN_XACML_CONFIG " +
+ "(CONFIG_KEY, CONFIG_VALUE, TENANT_ID) VALUES (:CONFIG_KEY;, :CONFIG_VALUE;, :TENANT_ID;)";
+ public static final String GET_POLICY_PDP_PRESENCE_SQL = "SELECT POLICY_ID FROM IDN_XACML_POLICY WHERE " +
+ "IS_IN_PDP=:IS_IN_PDP; AND POLICY_ID=:POLICY_ID; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_POLICY_PAP_PRESENCE_SQL = "SELECT POLICY_ID FROM IDN_XACML_POLICY WHERE " +
+ "IS_IN_PAP=:IS_IN_PAP; AND POLICY_ID=:POLICY_ID; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_PDP_POLICY_SQL =
+ "SELECT POLICY, POLICY_ORDER, IS_ACTIVE, VERSION FROM IDN_XACML_POLICY WHERE IS_IN_PDP=:IS_IN_PDP; " +
+ "AND POLICY_ID=:POLICY_ID; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_POLICY_PDP_PRESENCE_BY_VERSION_SQL = "SELECT POLICY_ID FROM IDN_XACML_POLICY " +
+ "WHERE IS_IN_PDP=:IS_IN_PDP; AND POLICY_ID=:POLICY_ID; AND VERSION=:VERSION; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_ALL_PDP_POLICIES_SQL = "SELECT POLICY_ID, POLICY, POLICY_ORDER, IS_ACTIVE, " +
+ "VERSION FROM IDN_XACML_POLICY WHERE IS_IN_PDP=:IS_IN_PDP; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_PDP_POLICY_IDS_SQL = "SELECT DISTINCT POLICY_ID FROM IDN_XACML_POLICY WHERE " +
+ "IS_IN_PDP=:IS_IN_PDP; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_PUBLISHED_POLICY_VERSION_SQL = "SELECT VERSION FROM IDN_XACML_POLICY WHERE " +
+ "IS_IN_PDP=:IS_IN_PDP; AND POLICY_ID=:POLICY_ID; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_ACTIVE_STATUS_AND_ORDER_SQL = "SELECT IS_ACTIVE, POLICY_ORDER FROM " +
+ "IDN_XACML_POLICY WHERE IS_IN_PDP=:IS_IN_PDP; AND POLICY_ID=:POLICY_ID; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_POLICY_COMBINING_ALGORITHM_SQL =
+ "SELECT CONFIG_VALUE FROM IDN_XACML_CONFIG WHERE CONFIG_KEY=:CONFIG_KEY; AND TENANT_ID=:TENANT_ID;";
+ public static final String UPDATE_ACTIVE_STATUS_SQL =
+ "UPDATE IDN_XACML_POLICY SET IS_ACTIVE=:IS_ACTIVE; WHERE POLICY_ID=:POLICY_ID; AND " +
+ "VERSION=:VERSION; AND TENANT_ID=:TENANT_ID;";
+ public static final String UPDATE_ORDER_SQL = "UPDATE IDN_XACML_POLICY SET POLICY_ORDER=:POLICY_ORDER; WHERE " +
+ "POLICY_ID=:POLICY_ID; AND VERSION=:VERSION; AND TENANT_ID=:TENANT_ID;";
+ public static final String DELETE_PUBLISHED_VERSIONS_SQL =
+ "UPDATE IDN_XACML_POLICY SET IS_IN_PDP=:IS_IN_PDP;, IS_ACTIVE=:IS_ACTIVE;, POLICY_ORDER=:POLICY_ORDER;" +
+ " WHERE IS_IN_PDP=:IS_IN_PDP_1; AND POLICY_ID=:POLICY_ID; AND TENANT_ID=:TENANT_ID;";
+ public static final String PUBLISH_POLICY_VERSION_SQL =
+ "UPDATE IDN_XACML_POLICY SET IS_IN_PDP=:IS_IN_PDP; WHERE POLICY_ID=:POLICY_ID; " +
+ "AND VERSION=:VERSION; AND TENANT_ID=:TENANT_ID;";
+ public static final String RESTORE_ACTIVE_STATUS_AND_ORDER_SQL = "UPDATE IDN_XACML_POLICY SET " +
+ "IS_ACTIVE=:IS_ACTIVE;, POLICY_ORDER=:POLICY_ORDER; WHERE POLICY_ID=:POLICY_ID; AND " +
+ "VERSION=:VERSION; AND TENANT_ID=:TENANT_ID;";
+ public static final String UPDATE_POLICY_COMBINING_ALGORITHM_SQL = "UPDATE IDN_XACML_CONFIG SET " +
+ "CONFIG_VALUE=:CONFIG_VALUE; WHERE CONFIG_KEY=:CONFIG_KEY; AND TENANT_ID=:TENANT_ID;";
+ public static final String DELETE_UNUSED_POLICY_SQL =
+ "DELETE FROM IDN_XACML_POLICY WHERE IS_IN_PAP=:IS_IN_PAP; AND IS_IN_PDP=:IS_IN_PDP; AND " +
+ "POLICY_ID=:POLICY_ID; AND TENANT_ID=:TENANT_ID; ";
+
+ /**
+ * DB queries related to subscribers.
+ */
+ public static final String CREATE_SUBSCRIBER_SQL =
+ "INSERT INTO IDN_XACML_SUBSCRIBER (SUBSCRIBER_ID, ENTITLEMENT_MODULE_NAME, TENANT_ID) VALUES " +
+ "(:SUBSCRIBER_ID;, :ENTITLEMENT_MODULE_NAME;, :TENANT_ID;)";
+ public static final String CREATE_SUBSCRIBER_PROPERTIES_SQL = "INSERT INTO IDN_XACML_SUBSCRIBER_PROPERTY " +
+ "(PROPERTY_ID, DISPLAY_NAME, PROPERTY_VALUE, IS_REQUIRED, DISPLAY_ORDER, IS_SECRET, " +
+ "PROPERTY_MODULE, SUBSCRIBER_ID, TENANT_ID) VALUES (:PROPERTY_ID;, :DISPLAY_NAME;, :PROPERTY_VALUE;, " +
+ ":IS_REQUIRED;, :DISPLAY_ORDER;, :IS_SECRET;, :PROPERTY_MODULE;, :SUBSCRIBER_ID;, :TENANT_ID;)";
+ public static final String GET_SUBSCRIBER_EXISTENCE_SQL = "SELECT SUBSCRIBER_ID FROM IDN_XACML_SUBSCRIBER " +
+ "WHERE SUBSCRIBER_ID=:SUBSCRIBER_ID; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_SUBSCRIBER_SQL = "SELECT s.SUBSCRIBER_ID, s.ENTITLEMENT_MODULE_NAME, s.TENANT_ID, " +
+ "p.PROPERTY_ID, p.DISPLAY_NAME, p.PROPERTY_VALUE, p.IS_REQUIRED, p.DISPLAY_ORDER, p.IS_SECRET, " +
+ "p.PROPERTY_MODULE FROM IDN_XACML_SUBSCRIBER s INNER JOIN " +
+ "IDN_XACML_SUBSCRIBER_PROPERTY p ON s.SUBSCRIBER_ID = p.SUBSCRIBER_ID AND s.TENANT_ID = p.TENANT_ID " +
+ "WHERE s.SUBSCRIBER_ID = :SUBSCRIBER_ID; AND s.TENANT_ID = :TENANT_ID;";
+ public static final String GET_SUBSCRIBER_IDS_SQL = "SELECT SUBSCRIBER_ID FROM IDN_XACML_SUBSCRIBER " +
+ "WHERE TENANT_ID=:TENANT_ID;";
+ public static final String UPDATE_SUBSCRIBER_MODULE_SQL = "UPDATE IDN_XACML_SUBSCRIBER " +
+ "SET ENTITLEMENT_MODULE_NAME=:ENTITLEMENT_MODULE_NAME; WHERE " +
+ "SUBSCRIBER_ID=:SUBSCRIBER_ID; AND TENANT_ID=:TENANT_ID;";
+ public static final String UPDATE_SUBSCRIBER_PROPERTIES_SQL = "UPDATE IDN_XACML_SUBSCRIBER_PROPERTY SET " +
+ "PROPERTY_VALUE=:PROPERTY_VALUE; WHERE PROPERTY_ID=:PROPERTY_ID; AND SUBSCRIBER_ID=:SUBSCRIBER_ID; AND " +
+ "TENANT_ID=:TENANT_ID;";
+ public static final String DELETE_SUBSCRIBER_SQL = "DELETE FROM IDN_XACML_SUBSCRIBER WHERE " +
+ "SUBSCRIBER_ID=:SUBSCRIBER_ID; AND TENANT_ID=:TENANT_ID;";
+
+ /**
+ * DB queries related to status.
+ */
+ public static final String CREATE_POLICY_STATUS_SQL = "INSERT INTO IDN_XACML_POLICY_STATUS (TYPE, IS_SUCCESS, " +
+ "USERNAME, TARGET, TARGET_ACTION, LOGGED_AT, MESSAGE, POLICY_ID, POLICY_VERSION, TENANT_ID) " +
+ "VALUES (:TYPE;, :IS_SUCCESS;, :USERNAME;, :TARGET;, :TARGET_ACTION;, :LOGGED_AT;, :MESSAGE;, " +
+ ":KEY;, :VERSION;, :TENANT_ID;)";
+ public static final String CREATE_SUBSCRIBER_STATUS_SQL = "INSERT INTO IDN_XACML_SUBSCRIBER_STATUS " +
+ "(TYPE, IS_SUCCESS, USERNAME, TARGET, TARGET_ACTION, LOGGED_AT, MESSAGE, SUBSCRIBER_ID, " +
+ "TENANT_ID) VALUES (:TYPE;, :IS_SUCCESS;, :USERNAME;, :TARGET;, :TARGET_ACTION;, :LOGGED_AT;, " +
+ ":MESSAGE;, :KEY;, :TENANT_ID;)";
+ public static final String GET_POLICY_STATUS_SQL = "SELECT POLICY_ID, TYPE, IS_SUCCESS, USERNAME, TARGET, " +
+ "TARGET_ACTION, LOGGED_AT, MESSAGE, POLICY_VERSION FROM IDN_XACML_POLICY_STATUS WHERE POLICY_ID=:KEY; " +
+ "AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_SUBSCRIBER_STATUS_SQL =
+ "SELECT SUBSCRIBER_ID, TYPE, IS_SUCCESS, USERNAME, TARGET, TARGET_ACTION, LOGGED_AT, MESSAGE FROM " +
+ "IDN_XACML_SUBSCRIBER_STATUS WHERE SUBSCRIBER_ID=:KEY; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_POLICY_STATUS_COUNT_SQL =
+ "SELECT COUNT(POLICY_ID) AS COUNT FROM IDN_XACML_POLICY_STATUS WHERE POLICY_ID=:KEY; AND " +
+ "TENANT_ID=:TENANT_ID;";
+ public static final String GET_SUBSCRIBER_STATUS_COUNT_SQL = "SELECT COUNT(SUBSCRIBER_ID) AS COUNT FROM " +
+ "IDN_XACML_SUBSCRIBER_STATUS WHERE SUBSCRIBER_ID=:KEY; AND TENANT_ID=:TENANT_ID;";
+ public static final String DELETE_POLICY_STATUS_SQL = "DELETE FROM IDN_XACML_POLICY_STATUS WHERE POLICY_ID=:KEY; " +
+ "AND TENANT_ID=:TENANT_ID;";
+ public static final String DELETE_SUBSCRIBER_STATUS_SQL = "DELETE FROM IDN_XACML_SUBSCRIBER_STATUS WHERE " +
+ "SUBSCRIBER_ID=:KEY; AND TENANT_ID=:TENANT_ID;";
+ public static final String DELETE_OLD_POLICY_STATUSES_MYSQL = "DELETE FROM IDN_XACML_POLICY_STATUS WHERE " +
+ "ID IN (SELECT ID FROM IDN_XACML_POLICY_STATUS WHERE POLICY_ID= :KEY; AND " +
+ "TENANT_ID= :TENANT_ID; ORDER BY ID ASC LIMIT :LIMIT;)";
+ public static final String DELETE_OLD_SUBSCRIBER_STATUSES_MYSQL =
+ "DELETE FROM IDN_XACML_SUBSCRIBER_STATUS WHERE ID " +
+ "IN (SELECT ID FROM IDN_XACML_SUBSCRIBER_STATUS WHERE SUBSCRIBER_ID= :KEY; AND " +
+ "TENANT_ID= :TENANT_ID; ORDER BY ID ASC LIMIT :LIMIT;)";
+ public static final String DELETE_OLD_POLICY_STATUSES_MSSQL =
+ "DELETE FROM IDN_XACML_POLICY_STATUS WHERE ID IN (SELECT ID FROM IDN_XACML_POLICY_STATUS WHERE " +
+ "POLICY_ID = :KEY; AND TENANT_ID = :TENANT_ID; ORDER BY ID ASC OFFSET 0 ROWS " +
+ "FETCH NEXT :LIMIT; ROWS ONLY)";
+ public static final String DELETE_OLD_SUBSCRIBER_STATUSES_MSSQL =
+ "DELETE FROM IDN_XACML_SUBSCRIBER_STATUS WHERE ID IN (SELECT ID FROM IDN_XACML_SUBSCRIBER_STATUS WHERE " +
+ "SUBSCRIBER_ID= :KEY; AND TENANT_ID=:TENANT_ID; ORDER BY ID ASC OFFSET 0 " +
+ "ROWS FETCH NEXT :LIMIT; ROWS ONLY)";
+ public static final String DELETE_OLD_POLICY_STATUSES_ORACLE =
+ "DELETE FROM IDN_XACML_POLICY_STATUS WHERE ID IN" +
+ " (SELECT ID FROM (SELECT ID FROM IDN_XACML_POLICY_STATUS WHERE POLICY_ID= :KEY; AND" +
+ " TENANT_ID=:TENANT_ID; ORDER BY ID ASC) WHERE ROWNUM <= :LIMIT;)";
+ public static final String DELETE_OLD_SUBSCRIBER_STATUSES_ORACLE =
+ "DELETE FROM IDN_XACML_SUBSCRIBER_STATUS WHERE ID " +
+ "IN (SELECT ID FROM (SELECT ID FROM IDN_XACML_SUBSCRIBER_STATUS WHERE SUBSCRIBER_ID= :KEY; " +
+ "AND TENANT_ID=:TENANT_ID; ORDER BY ID ASC) WHERE ROWNUM <= :LIMIT;)";
+
+ /**
+ * DB queries related to policy version management.
+ */
+ public static final String GET_LATEST_POLICY_VERSION_SQL =
+ "SELECT MAX(VERSION) AS VERSION FROM IDN_XACML_POLICY " +
+ "WHERE IS_IN_PAP=:IS_IN_PAP; AND POLICY_ID=:POLICY_ID; AND TENANT_ID=:TENANT_ID;";
+ public static final String GET_POLICY_VERSIONS_SQL = "SELECT VERSION FROM IDN_XACML_POLICY WHERE " +
+ "POLICY_ID=:POLICY_ID; AND TENANT_ID=:TENANT_ID;";
+ }
+}
diff --git a/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/PersistenceManagerFactory.java b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/PersistenceManagerFactory.java
new file mode 100644
index 000000000000..4dce296eca39
--- /dev/null
+++ b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/PersistenceManagerFactory.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * 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.entitlement.persistence;
+
+import org.apache.commons.lang.StringUtils;
+import org.wso2.carbon.identity.core.util.IdentityUtil;
+import org.wso2.carbon.identity.entitlement.PAPStatusDataHandler;
+import org.wso2.carbon.identity.entitlement.SimplePAPStatusDataHandler;
+
+import static org.wso2.carbon.identity.entitlement.PDPConstants.POLICY_STORAGE_CONFIG;
+
+public class PersistenceManagerFactory {
+
+ private static final String POLICY_STORAGE_TYPE = IdentityUtil.getProperty(POLICY_STORAGE_CONFIG);
+ private static final String HYBRID = "hybrid";
+ private static final String REGISTRY = "registry";
+
+ private PersistenceManagerFactory() {
+
+ }
+
+ public static PolicyPersistenceManager getPolicyPersistenceManager() {
+
+ PolicyPersistenceManager defaultPolicyPersistenceManager = new JDBCPolicyPersistenceManager();
+ if (StringUtils.isNotBlank(POLICY_STORAGE_TYPE)) {
+ switch (POLICY_STORAGE_TYPE) {
+ case HYBRID:
+ return new HybridPolicyPersistenceManager();
+ case REGISTRY:
+ return new RegistryPolicyPersistenceManager();
+ default:
+ return defaultPolicyPersistenceManager;
+ }
+ }
+ return defaultPolicyPersistenceManager;
+ }
+
+ public static ConfigPersistenceManager getConfigPersistenceManager() {
+
+ ConfigPersistenceManager defaultConfigPersistenceManager = new JDBCConfigPersistenceManager();
+ if (StringUtils.isNotBlank(POLICY_STORAGE_TYPE)) {
+ switch (POLICY_STORAGE_TYPE) {
+ case HYBRID:
+ return new HybridConfigPersistenceManager();
+ case REGISTRY:
+ return new RegistryConfigPersistenceManager();
+ default:
+ return defaultConfigPersistenceManager;
+ }
+ }
+ return defaultConfigPersistenceManager;
+ }
+
+ public static SubscriberPersistenceManager getSubscriberPersistenceManager() {
+
+ SubscriberPersistenceManager defaultSubscriberPersistenceManager = new JDBCSubscriberPersistenceManager();
+ if (StringUtils.isNotBlank(POLICY_STORAGE_TYPE)) {
+ switch (POLICY_STORAGE_TYPE) {
+ case HYBRID:
+ return new HybridSubscriberPersistenceManager();
+ case REGISTRY:
+ return new RegistrySubscriberPersistenceManager();
+ default:
+ return defaultSubscriberPersistenceManager;
+ }
+ }
+ return defaultSubscriberPersistenceManager;
+ }
+
+ public static PAPStatusDataHandler getPAPStatusDataHandler() {
+
+ PAPStatusDataHandler defaultPAPStatusDataHandler = new JDBCSimplePAPStatusDataHandler();
+ if (StringUtils.isNotBlank(POLICY_STORAGE_TYPE)) {
+ switch (POLICY_STORAGE_TYPE) {
+ case HYBRID:
+ return new HybridPAPStatusDataHandler();
+ case REGISTRY:
+ return new SimplePAPStatusDataHandler();
+ default:
+ return defaultPAPStatusDataHandler;
+ }
+ }
+ return defaultPAPStatusDataHandler;
+ }
+}
+
diff --git a/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/PolicyPersistenceManager.java b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/PolicyPersistenceManager.java
new file mode 100644
index 000000000000..4a5b9026bc86
--- /dev/null
+++ b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/PolicyPersistenceManager.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * 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.entitlement.persistence;
+
+import org.wso2.carbon.identity.entitlement.EntitlementException;
+import org.wso2.carbon.identity.entitlement.dto.PolicyDTO;
+import org.wso2.carbon.identity.entitlement.dto.PolicyStoreDTO;
+import org.wso2.carbon.identity.entitlement.policy.store.PolicyStoreManageModule;
+
+import java.util.List;
+
+/**
+ * This interface supports the management of XACML policies.
+ */
+public interface PolicyPersistenceManager extends PolicyStoreManageModule {
+
+ /**
+ * Adds or updates the given policy.
+ *
+ * @param policy policy
+ * @param isFromPapAction true if the operation originated from a PAP action, false if it is from a PDP action.
+ * @throws EntitlementException If an error occurs
+ */
+ void addOrUpdatePolicy(PolicyDTO policy, boolean isFromPapAction) throws EntitlementException;
+
+ /**
+ * Gets the requested policy.
+ *
+ * @param policyId policy ID
+ * @return policyDTO
+ * @throws EntitlementException If an error occurs
+ */
+ PolicyDTO getPAPPolicy(String policyId) throws EntitlementException;
+
+ /**
+ * Gets the requested policy list.
+ *
+ * @param policyIds policy ID list
+ * @return policyDTO
+ * @throws EntitlementException If an error occurs
+ */
+ List getPAPPolicies(List policyIds) throws EntitlementException;
+
+ /**
+ * Gets the requested policy version.
+ *
+ * @param policyId policy ID
+ * @param version policy version
+ * @return requested policy
+ * @throws EntitlementException If an error occurs
+ */
+ PolicyDTO getPolicy(String policyId, String version) throws EntitlementException;
+
+ /**
+ * Gets all versions of the given policy ID.
+ *
+ * @param policyId policy ID
+ * @return array of policy versions
+ */
+ String[] getVersions(String policyId);
+
+ /**
+ * Lists all PAP policy IDs.
+ *
+ * @return list of policy IDs
+ * @throws EntitlementException If an error occurs
+ */
+ List listPolicyIds() throws EntitlementException;
+
+ /**
+ * Removes the given policy.
+ *
+ * @param policyId policy ID
+ * @throws EntitlementException If an error occurs
+ */
+ void removePolicy(String policyId) throws EntitlementException;
+
+ /**
+ * Gets the requested published policy.
+ *
+ * @param policyId policy ID
+ * @return requested policy
+ */
+ PolicyStoreDTO getPublishedPolicy(String policyId);
+
+ /**
+ * Lists all published policy IDs.
+ *
+ * @return list of published policy IDs
+ * @throws EntitlementException If an error occurs
+ */
+ List listPublishedPolicyIds() throws EntitlementException;
+}
diff --git a/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/RegistryConfigPersistenceManager.java b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/RegistryConfigPersistenceManager.java
new file mode 100644
index 000000000000..c3e313fe9d63
--- /dev/null
+++ b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/RegistryConfigPersistenceManager.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * 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.entitlement.persistence;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.context.CarbonContext;
+import org.wso2.carbon.identity.entitlement.EntitlementException;
+import org.wso2.carbon.identity.entitlement.PDPConstants;
+import org.wso2.carbon.identity.entitlement.internal.EntitlementServiceComponent;
+import org.wso2.carbon.registry.core.Collection;
+import org.wso2.carbon.registry.core.Registry;
+import org.wso2.carbon.registry.core.exceptions.RegistryException;
+
+import static org.wso2.carbon.identity.entitlement.PDPConstants.GLOBAL_POLICY_COMBINING_ALGORITHM;
+
+/**
+ * This implementation handles the Global Policy Combining Algorithm management in the Registry.
+ */
+public class RegistryConfigPersistenceManager implements ConfigPersistenceManager {
+
+ // The logger that is used for all messages
+ private static final Log LOG = LogFactory.getLog(RegistryConfigPersistenceManager.class);
+ private static final String POLICY_DATA_COLLECTION = PDPConstants.ENTITLEMENT_POLICY_DATA;
+ private final Registry registry;
+
+ public RegistryConfigPersistenceManager() {
+
+ int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
+ registry = EntitlementServiceComponent.getGovernanceRegistry(tenantId);
+ }
+
+ /**
+ * Sets the global policy combining algorithm.
+ *
+ * @param policyCombiningAlgorithm policy combining algorithm name.
+ * @return true if the policy combining algorithm is updated, false if the policy combining algorithm is added.
+ * @throws EntitlementException If an error occurs.
+ */
+ @Override
+ public boolean addOrUpdateGlobalPolicyAlgorithm(String policyCombiningAlgorithm) throws EntitlementException {
+
+ boolean isUpdate = false;
+ try {
+ Collection policyCollection;
+ if (registry.resourceExists(POLICY_DATA_COLLECTION)) {
+ policyCollection = (Collection) registry.get(POLICY_DATA_COLLECTION);
+ } else {
+ policyCollection = registry.newCollection();
+ }
+ if (StringUtils.isNotBlank(policyCollection.getProperty(GLOBAL_POLICY_COMBINING_ALGORITHM))) {
+ isUpdate = true;
+ }
+ policyCollection.setProperty(GLOBAL_POLICY_COMBINING_ALGORITHM, policyCombiningAlgorithm);
+ registry.put(POLICY_DATA_COLLECTION, policyCollection);
+
+ } catch (RegistryException e) {
+ throw new EntitlementException("Error while updating global policy combining algorithm in policy store", e);
+ }
+ return isUpdate;
+ }
+
+ /**
+ * Gets the policy combining algorithm name.
+ *
+ * @return global policy combining algorithm name
+ */
+ @Override
+ public String getGlobalPolicyAlgorithmName() {
+
+ String algorithm = null;
+ try {
+ if (registry.resourceExists(POLICY_DATA_COLLECTION)) {
+ Collection collection = (Collection) registry.get(POLICY_DATA_COLLECTION);
+ algorithm = collection.getProperty(GLOBAL_POLICY_COMBINING_ALGORITHM);
+ }
+ } catch (RegistryException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(e);
+ }
+ }
+
+ // set default
+ if (algorithm == null) {
+ algorithm = PDPConstants.Algorithms.DENY_OVERRIDES;
+ }
+
+ return algorithm;
+ }
+
+ /**
+ * Deletes the global policy combining algorithm.
+ *
+ * @throws EntitlementException If an error occurs
+ */
+ public void deleteGlobalPolicyAlgorithm() throws EntitlementException {
+
+ try {
+ if (registry.resourceExists(POLICY_DATA_COLLECTION)) {
+ registry.delete(POLICY_DATA_COLLECTION);
+ }
+ } catch (RegistryException e) {
+ throw new EntitlementException("Error while deleting global policy combining algorithm in policy store", e);
+ }
+ }
+}
diff --git a/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/RegistryPolicyPersistenceManager.java b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/RegistryPolicyPersistenceManager.java
new file mode 100644
index 000000000000..94938b1a1106
--- /dev/null
+++ b/components/entitlement/org.wso2.carbon.identity.entitlement/src/main/java/org/wso2/carbon/identity/entitlement/persistence/RegistryPolicyPersistenceManager.java
@@ -0,0 +1,1247 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * 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.entitlement.persistence;
+
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.util.AXIOMUtil;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.balana.AbstractPolicy;
+import org.wso2.carbon.context.CarbonContext;
+import org.wso2.carbon.identity.entitlement.EntitlementException;
+import org.wso2.carbon.identity.entitlement.EntitlementUtil;
+import org.wso2.carbon.identity.entitlement.PDPConstants;
+import org.wso2.carbon.identity.entitlement.PolicyOrderComparator;
+import org.wso2.carbon.identity.entitlement.dto.AttributeDTO;
+import org.wso2.carbon.identity.entitlement.dto.PolicyDTO;
+import org.wso2.carbon.identity.entitlement.dto.PolicyStoreDTO;
+import org.wso2.carbon.identity.entitlement.internal.EntitlementServiceComponent;
+import org.wso2.carbon.identity.entitlement.pap.PAPPolicyReader;
+import org.wso2.carbon.identity.entitlement.policy.PolicyAttributeBuilder;
+import org.wso2.carbon.identity.entitlement.policy.finder.AbstractPolicyFinderModule;
+import org.wso2.carbon.identity.entitlement.policy.finder.PolicyFinderModule;
+import org.wso2.carbon.registry.core.Collection;
+import org.wso2.carbon.registry.core.Registry;
+import org.wso2.carbon.registry.core.RegistryConstants;
+import org.wso2.carbon.registry.core.Resource;
+import org.wso2.carbon.registry.core.exceptions.RegistryException;
+import org.wso2.carbon.registry.core.exceptions.ResourceNotFoundException;
+import org.wso2.carbon.registry.core.utils.RegistryUtils;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.xml.stream.XMLStreamException;
+
+import static org.wso2.carbon.identity.entitlement.PDPConstants.MODULE_NAME;
+
+/**
+ * This implementation handles the XACML policy management in the Registry.
+ */
+public class RegistryPolicyPersistenceManager extends AbstractPolicyFinderModule implements PolicyPersistenceManager {
+
+ // The logger that is used for all messages
+ private static final Log LOG = LogFactory.getLog(RegistryPolicyPersistenceManager.class);
+ private static final String KEY_VALUE_POLICY_META_DATA = "policyMetaData";
+ private static final String POLICY_STORE_PATH = "policyStorePath";
+ private static final String DEFAULT_POLICY_STORE_PATH = "/repository/identity/entitlement/policy/pdp/";
+ private static final String INVALID_POLICY_VERSION = "Invalid policy version";
+ private static final String ERROR_RETRIEVING_POLICIES_FROM_POLICY_FINDER =
+ "Policies can not be retrieved from registry policy finder module";
+ private static final String INVALID_ENTITLEMENT_POLICY = "Trying to access an entitlement policy %s which does " +
+ "not exist";
+ private static final String ERROR_PUBLISHING_POLICY = "Error while publishing policy";
+ private String policyStorePath;
+ private final int maxVersions;
+
+ public RegistryPolicyPersistenceManager() {
+
+ maxVersions = EntitlementUtil.getMaxNoOfPolicyVersions();
+ }
+
+ @Override
+ public void init(Properties properties) {
+
+ policyStorePath = properties.getProperty(POLICY_STORE_PATH);
+ if (policyStorePath == null) {
+ policyStorePath = DEFAULT_POLICY_STORE_PATH;
+ }
+ }
+
+ /**
+ * Adds or updates the given PAP policy.
+ *
+ * @param policy policy.
+ * @param isFromPapAction true if the operation originated from a PAP action, false if it is from a PDP action.
+ * @throws EntitlementException If an error occurs
+ */
+ @Override
+ public void addOrUpdatePolicy(PolicyDTO policy, boolean isFromPapAction) throws EntitlementException {
+
+ if (isFromPapAction) {
+ String version = createVersion(policy);
+ policy.setVersion(version);
+ addOrUpdatePAPPolicy(policy, policy.getVersion(), PDPConstants.ENTITLEMENT_POLICY_VERSION +
+ policy.getPolicyId() + RegistryConstants.PATH_SEPARATOR);
+ }
+ addOrUpdatePAPPolicy(policy, policy.getPolicyId(), PDPConstants.ENTITLEMENT_POLICY_PAP);
+ }
+
+ /**
+ * Gets the requested policy.
+ *
+ * @param policyId policy ID
+ * @return policyDTO
+ * @throws EntitlementException If an error occurs
+ */
+ @Override
+ public PolicyDTO getPAPPolicy(String policyId) throws EntitlementException {
+
+ String path = PDPConstants.ENTITLEMENT_POLICY_PAP + policyId;
+ return getPolicyDTO(policyId, path);
+ }
+
+ /**
+ * Gets the requested policy list.
+ *
+ * @param policyIds policy ID list
+ * @return policyDTO
+ * @throws EntitlementException If an error occurs
+ */
+ @Override
+ public List getPAPPolicies(List policyIds) throws EntitlementException {
+
+ if (policyIds == null || policyIds.isEmpty()) {
+ return new ArrayList<>();
+ }
+ List policyDTOs = new ArrayList<>();
+ for (String policyId : policyIds) {
+ policyDTOs.add(getPAPPolicy(policyId));
+ }
+ return policyDTOs;
+ }
+
+ /**
+ * Gets the requested policy version.
+ *
+ * @param policyId policy ID
+ * @param version policy version
+ * @return policyDTO
+ * @throws EntitlementException If an error occurs
+ */
+ @Override
+ public PolicyDTO getPolicy(String policyId, String version) throws EntitlementException {
+
+ // Zero indicates the current version
+ if (version == null || version.trim().isEmpty()) {
+ try {
+ Registry registry = getRegistry();
+ Collection collection = (Collection) registry.get(PDPConstants.ENTITLEMENT_POLICY_VERSION + policyId);
+ if (collection != null) {
+ version = collection.getProperty(PDPConstants.POLICY_VERSION);
+ }
+ } catch (RegistryException e) {
+ throw new EntitlementException(INVALID_POLICY_VERSION, e);
+ }
+ }
+
+ String collection = PDPConstants.ENTITLEMENT_POLICY_VERSION + policyId + RegistryConstants.PATH_SEPARATOR;
+ String path = collection + version;
+ PolicyDTO dto = getPolicyDTO(policyId, path);
+
+ if (dto == null) {
+ throw new EntitlementException(INVALID_POLICY_VERSION);
+ }
+ return dto;
+ }
+
+ /**
+ * Gets all versions of the given policy ID.
+ *
+ * @param policyId policy ID
+ * @return array of policy versions
+ */
+ @Override
+ public String[] getVersions(String policyId) {
+
+ List versions = new ArrayList<>();
+ Collection collection = null;
+ try {
+ try {
+ Registry registry = getRegistry();
+ collection = (Collection) registry.get(PDPConstants.ENTITLEMENT_POLICY_VERSION + policyId);
+ } catch (ResourceNotFoundException e) {
+ // ignore
+ }
+ if (collection != null && collection.getChildren() != null) {
+ String[] children = collection.getChildren();
+ for (String child : children) {
+ versions.add(RegistryUtils.getResourceName(child));
+ }
+ }
+ } catch (RegistryException e) {
+ LOG.error(String.format("Error while retrieving policy versions for policy %s", policyId), e);
+ }
+ return versions.toArray(new String[0]);
+
+ }
+
+ /**
+ * Gets the name of the module.
+ *
+ * @return name as String
+ */
+ @Override
+ public String getModuleName() {
+
+ return MODULE_NAME;
+ }
+
+ /**
+ * Gets the policy for the given policy ID.
+ *
+ * @param policyId policy id as a string value
+ * @return policy as string
+ */
+ @Override
+ public String getPolicy(String policyId) {
+
+ PolicyStoreDTO dto = getPublishedPolicy(policyId);
+ return dto.getPolicy();
+ }
+
+ /**
+ * Gets the policy order.
+ *
+ * @param policyId policy id as a string value
+ * @return policy order
+ */
+ @Override
+ public int getPolicyOrder(String policyId) {
+
+ PolicyStoreDTO dto = getPublishedPolicy(policyId);
+ return dto.getPolicyOrder();
+ }
+
+ /**
+ * Gets all supported active policies.
+ * If policy ordering is supported by the module itself, these policies must be ordered.
+ *
+ * @return array of policies as Strings
+ */
+ @Override
+ public String[] getActivePolicies() {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Retrieving of Active policies are started at %s", new Date()));
+ }
+
+ List policies = new ArrayList<>();
+
+ try {
+ PolicyStoreDTO[] policyDTOs = getAllPolicies(true, true);
+ for (PolicyStoreDTO dto : policyDTOs) {
+ if (dto.getPolicy() != null) {
+ policies.add(dto.getPolicy());
+ }
+ }
+ } catch (EntitlementException e) {
+ LOG.error(ERROR_RETRIEVING_POLICIES_FROM_POLICY_FINDER, e);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Retrieving of Active policies are finished at %s", new Date()));
+ }
+
+ return policies.toArray(new String[0]);
+ }
+
+ /**
+ * Gets all supported policy ids.
+ * If policy ordering is supported by the module itself, these policy ids must be ordered.
+ *
+ * @return array of policy ids as Strings
+ */
+ @Override
+ public String[] getOrderedPolicyIdentifiers() {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Retrieving of Order Policy Ids are started at %s ", new Date()));
+ }
+
+ List policies = new ArrayList<>();
+
+ try {
+ PolicyStoreDTO[] policyDTOs = getAllPolicies(false, true);
+ for (PolicyStoreDTO dto : policyDTOs) {
+ if (dto.getPolicy() != null) {
+ policies.add(dto.getPolicyId());
+ }
+ }
+ } catch (EntitlementException e) {
+ LOG.error(ERROR_RETRIEVING_POLICIES_FROM_POLICY_FINDER, e);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Retrieving of Order Policy Ids are finished at %s ", new Date()));
+ }
+
+ return policies.toArray(new String[0]);
+
+ }
+
+ /**
+ * Gets all policy ids.
+ *
+ * @return array of policy ids as Strings
+ */
+ @Override
+ public String[] getPolicyIdentifiers() {
+
+ String[] policyIds = null;
+ try {
+ policyIds = listPublishedPolicyIds().toArray(new String[0]);
+ } catch (EntitlementException e) {
+ LOG.error("Policy identifiers can not be retrieved from registry policy finder module", e);
+ }
+ return policyIds;
+ }
+
+ /**
+ * Gets reference policy for the given policy ID.
+ * Reference policy can not be with PDP policy store, may be in some external policy store.
+ * Therefore, a new method has been added to retrieve reference policies.
+ *
+ * @param policyId policy id as String value
+ * @return reference policy as String
+ */
+ @Override
+ public String getReferencedPolicy(String policyId) {
+
+ // Retrieves for policies that are not active
+ PolicyStoreDTO dto = getPublishedPolicy(policyId);
+ if (dto != null && dto.getPolicy() != null && !dto.isActive()) {
+ return dto.getPolicy();
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets attributes that are used for policy searching.
+ *
+ * @param identifier unique identifier to separate out search attributes
+ * @param givenAttribute pre-given attributes to retrieve other attributes
+ * @return return search attributes based on a given policy, Map of policy id with search attributes.
+ */
+ @Override
+ public Map> getSearchAttributes(String identifier, Set givenAttribute) {
+
+ try {
+ PolicyStoreDTO[] policyDTOs = getAllPolicies(true, true);
+ List policyIds = new ArrayList<>();
+ for (PolicyStoreDTO policyStoreDTO : policyDTOs) {
+ policyIds.add(policyStoreDTO.getPolicyId());
+ }
+ List policyDTOList = getPAPPolicies(policyIds);
+ if (policyDTOs.length > 0) {
+ return EntitlementUtil.getAttributesFromPolicies(policyDTOList.toArray(new PolicyDTO[0]));
+ }
+ } catch (EntitlementException e) {
+ LOG.error(ERROR_RETRIEVING_POLICIES_FROM_POLICY_FINDER, e);
+ }
+
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Gets support attribute searching scheme of the module.
+ *
+ * @return return scheme identifier value
+ */
+ @Override
+ public int getSupportedSearchAttributesScheme() {
+
+ return PolicyFinderModule.COMBINATIONS_BY_CATEGORY_AND_PARAMETER;
+ }
+
+ /**
+ * Lists all PAP policy IDs.
+ *
+ * @return list of policy IDs
+ * @throws EntitlementException If an error occurs
+ */
+ @Override
+ public List listPolicyIds() throws EntitlementException {
+
+ String path = PDPConstants.ENTITLEMENT_POLICY_PAP;
+ return listAllPolicyIds(path);
+
+ }
+
+ /**
+ * Removes the given policy from PAP.
+ *
+ * @param policyId policy ID
+ * @throws EntitlementException If an error occurs
+ */
+ @Override
+ public void removePolicy(String policyId) throws EntitlementException {
+
+ String path;
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Removing entitlement policy %s", policyId));
+ }
+
+ try {
+ path = PDPConstants.ENTITLEMENT_POLICY_PAP + policyId;
+ Registry registry = getRegistry();
+ if (!registry.resourceExists(path)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format(INVALID_ENTITLEMENT_POLICY, policyId));
+ }
+ return;
+ }
+ registry.delete(path);
+
+ // Removes versions
+ if (registry.resourceExists(PDPConstants.ENTITLEMENT_POLICY_VERSION + policyId)) {
+ registry.delete(PDPConstants.ENTITLEMENT_POLICY_VERSION + policyId);
+ }
+
+ } catch (RegistryException e) {
+ throw new EntitlementException(String.format("Error while removing policy %s from PAP policy store",
+ policyId), e);
+ }
+
+ }
+
+ /**
+ * Publishes the given policy.
+ *
+ * @param policy policy to be published
+ * @throws EntitlementException If an error occurs
+ */
+ @Override
+ public void addPolicy(PolicyStoreDTO policy) throws EntitlementException {
+
+ String policyPath;
+ Collection policyCollection;
+ Resource resource;
+ String papPath;
+ Resource papResource;
+
+ if (policy == null || StringUtils.isBlank(policy.getPolicyId())) {
+ throw new EntitlementException("Policy can not be null");
+ }
+
+ try {
+
+ // Restricts publishing policies that are not in PAP
+ papPath = PDPConstants.ENTITLEMENT_POLICY_PAP + policy.getPolicyId();
+ Registry registry = getRegistry();
+ if (!registry.resourceExists(papPath)) {
+ throw new EntitlementException("Policies that are not included in the PAP, cannot be published");
+ }
+
+ // Publishes policy to PDP
+ if (registry.resourceExists(policyStorePath)) {
+ policyCollection = (Collection) registry.get(policyStorePath);
+ } else {
+ policyCollection = registry.newCollection();
+ }
+ registry.put(policyStorePath, policyCollection);
+
+ policyPath = policyStorePath + policy.getPolicyId();
+ if (registry.resourceExists(policyPath)) {
+ resource = registry.get(policyPath);
+ } else {
+ resource = registry.newResource();
+ }
+
+ if (policy.getPolicy() != null && !policy.getPolicy().trim().isEmpty()) {
+ resource.setContent(policy.getPolicy());
+ resource.setMediaType(PDPConstants.REGISTRY_MEDIA_TYPE);
+ AttributeDTO[] attributeDTOs = policy.getAttributeDTOs();
+ if (attributeDTOs != null && EntitlementUtil.isPolicyMetadataStoringEnabled()) {
+ setAttributesAsProperties(attributeDTOs, resource);
+ }
+ }
+ if (policy.isSetActive()) {
+ resource.setProperty("active", Boolean.toString(policy.isActive()));
+ }
+ if (policy.isSetOrder()) {
+ int order = policy.getPolicyOrder();
+ if (order > 0) {
+ resource.setProperty("order", Integer.toString(order));
+ }
+ }
+ if (resource.getContent() == null) {
+ LOG.info(String.format("Prevented adding null content to resource %s", policyPath));
+ return;
+ }
+ // Store policy metadata based on the configured property.
+ if (!EntitlementUtil.isPolicyMetadataStoringEnabled()) {
+ for (Map.Entry