/*
* Copyright (c) 2005-2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.pdp;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
import org.wso2.balana.Balana;
import org.wso2.balana.PDP;
import org.wso2.balana.PDPConfig;
import org.wso2.balana.ParsingException;
import org.wso2.balana.ctx.AbstractRequestCtx;
import org.wso2.balana.ctx.RequestCtxFactory;
import org.wso2.balana.ctx.ResponseCtx;
import org.wso2.balana.finder.AttributeFinder;
import org.wso2.balana.finder.AttributeFinderModule;
import org.wso2.balana.finder.PolicyFinder;
import org.wso2.balana.finder.PolicyFinderModule;
import org.wso2.balana.finder.ResourceFinder;
import org.wso2.balana.finder.ResourceFinderModule;
import org.wso2.balana.finder.impl.CurrentEnvModule;
import org.wso2.balana.finder.impl.SelectorModule;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.identity.base.IdentityConstants;
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.PDPConstants;
import org.wso2.carbon.identity.entitlement.cache.DecisionCache;
import org.wso2.carbon.identity.entitlement.cache.EntitlementEngineCache;
import org.wso2.carbon.identity.entitlement.cache.PolicyCache;
import org.wso2.carbon.identity.entitlement.cache.SimpleDecisionCache;
import org.wso2.carbon.identity.entitlement.internal.EntitlementServiceComponent;
import org.wso2.carbon.identity.entitlement.pap.store.PAPPolicyFinder;
import org.wso2.carbon.identity.entitlement.pap.store.PAPPolicyStore;
import org.wso2.carbon.identity.entitlement.pap.store.PAPPolicyStoreReader;
import org.wso2.carbon.identity.entitlement.pip.CarbonAttributeFinder;
import org.wso2.carbon.identity.entitlement.pip.CarbonResourceFinder;
import org.wso2.carbon.identity.entitlement.pip.PIPExtension;
import org.wso2.carbon.identity.entitlement.policy.PolicyRequestBuilder;
import org.wso2.carbon.identity.entitlement.policy.finder.CarbonPolicyFinder;
import org.wso2.carbon.identity.entitlement.policy.search.PolicySearch;
import org.wso2.carbon.utils.CarbonUtils;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class EntitlementEngine {
private PolicyFinder papPolicyFinder;
private CarbonAttributeFinder carbonAttributeFinder;
private CarbonResourceFinder carbonResourceFinder;
private PolicyFinder carbonPolicyFinder;
private PolicySearch policySearch;
private PDP pdp;
private PDP pdpTest;
private Balana balana;
private int tenantId;
private static final Object lock = new Object();
private boolean pdpDecisionCacheEnable;
private List<AttributeFinderModule> attributeModules = new ArrayList<AttributeFinderModule>();
private List<ResourceFinderModule> resourceModules = new ArrayList<ResourceFinderModule>();
private static EntitlementEngineCache entitlementEngines = EntitlementEngineCache.getInstance();
private static EntitlementEngine entitlementEngine;
private DecisionCache decisionCache = null;
private PolicyCache policyCache = null;
private SimpleDecisionCache simpleDecisionCache = null;
private static Log log = LogFactory.getLog(EntitlementEngine.class);
public PolicyCache getPolicyCache() {
return policyCache;
}
public void setPolicyCache(PolicyCache policyCache) {
this.policyCache = policyCache;
}
public void clearDecisionCache() {
this.decisionCache.clear();
}
/**
* Get a EntitlementEngine instance for that tenant. This method will return an
* EntitlementEngine instance if exists, or creates a new one
*
* @return EntitlementEngine instance for that tenant
*/
public static EntitlementEngine getInstance() {
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
if (tenantId == MultitenantConstants.SUPER_TENANT_ID) {
if (entitlementEngine == null) {
synchronized (lock) {
if (entitlementEngine == null) {
entitlementEngine = new EntitlementEngine(tenantId);
}
}
}
return entitlementEngine;
}
if (!entitlementEngines.contains(tenantId)) {
synchronized (lock) {
if (!entitlementEngines.contains(tenantId)) {
entitlementEngines.put(tenantId, new EntitlementEngine(tenantId));
}
}
}
return entitlementEngines.get(tenantId);
}
private EntitlementEngine(int tenantId) {
boolean isPDP = Boolean.parseBoolean((String) EntitlementServiceComponent.getEntitlementConfig().
getEngineProperties().get(PDPConstants.PDP_ENABLE));
boolean isPAP = Boolean.parseBoolean((String) EntitlementServiceComponent.getEntitlementConfig().
getEngineProperties().get(PDPConstants.PAP_ENABLE));
boolean pdpMultipleDecision = Boolean.parseBoolean((String) EntitlementServiceComponent.
getEntitlementConfig().getEngineProperties().get(PDPConstants.MULTIPLE_DECISION_PROFILE_ENABLE));
if (!isPAP && !isPDP) {
isPAP = true;
}
boolean balanaConfig = Boolean.parseBoolean((String) EntitlementServiceComponent.getEntitlementConfig().
getEngineProperties().get(PDPConstants.BALANA_CONFIG_ENABLE));
if (balanaConfig) {
System.setProperty("org.wso2.balana.PDPConfigFile", CarbonUtils.getCarbonConfigDirPath()
+ File.separator + "security" + File.separator +
"balana-config.xml");
}
// if PDP config file is not configured, then balana instance is created from default configurations
balana = Balana.getInstance();
setUpAttributeFinders();
setUpResourceFinders();
setUPPolicyFinder();
this.tenantId = tenantId;
Properties properties = EntitlementServiceComponent.getEntitlementConfig().getEngineProperties();
pdpDecisionCacheEnable = Boolean.parseBoolean(properties.getProperty(PDPConstants.DECISION_CACHING));
int pdpDecisionCachingInterval = -1;
if (pdpDecisionCacheEnable) {
String cacheInterval = properties.getProperty(PDPConstants.DECISION_CACHING_INTERVAL);
if (cacheInterval != null) {
try {
pdpDecisionCachingInterval = Integer.parseInt(cacheInterval.trim());
} catch (Exception e) {
//ignore
}
}
}
int pdpPolicyCachingInterval = -1;
String policyCacheInterval = properties.getProperty(PDPConstants.POLICY_CACHING_INTERVAL);
if (policyCacheInterval != null) {
try {
pdpPolicyCachingInterval = Integer.parseInt(policyCacheInterval.trim());
} catch (Exception e) {
//ignore
}
}
//init caches
decisionCache = new DecisionCache(pdpDecisionCachingInterval);
simpleDecisionCache = new SimpleDecisionCache(pdpDecisionCachingInterval);
this.policyCache = new PolicyCache(pdpPolicyCachingInterval);
// policy search
policySearch = new PolicySearch(pdpDecisionCacheEnable, pdpDecisionCachingInterval);
// Finally, initialize
if (isPAP) {
// Test PDP with all finders but policy finder is different
PolicyFinder policyFinder = new PolicyFinder();
Set<PolicyFinderModule> policyModules = new HashSet<PolicyFinderModule>();
PAPPolicyFinder papPolicyFinder = new PAPPolicyFinder(new PAPPolicyStoreReader(new PAPPolicyStore()));
policyModules.add(papPolicyFinder);
policyFinder.setModules(policyModules);
this.papPolicyFinder = policyFinder;
AttributeFinder attributeFinder = new AttributeFinder();
attributeFinder.setModules(attributeModules);
ResourceFinder resourceFinder = new ResourceFinder();
resourceFinder.setModules(resourceModules);
PDPConfig pdpConfig = new PDPConfig(attributeFinder, policyFinder, resourceFinder, true);
pdpTest = new PDP(pdpConfig);
}
if (isPDP) {
// Actual PDP with all finders but policy finder is different
AttributeFinder attributeFinder = new AttributeFinder();
attributeFinder.setModules(attributeModules);
ResourceFinder resourceFinder = new ResourceFinder();
resourceFinder.setModules(resourceModules);
PDPConfig pdpConfig =
new PDPConfig(attributeFinder, carbonPolicyFinder, resourceFinder, pdpMultipleDecision);
pdp = new PDP(pdpConfig);
}
}
/**
* Test request for PDP
*
* @param xacmlRequest XACML request as String
* @return response as String
*/
public String test(String xacmlRequest) {
if (log.isDebugEnabled() && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.XACML_REQUEST)) {
log.debug("XACML Request : " + xacmlRequest);
}
String xacmlResponse = pdpTest.evaluate(xacmlRequest);
if (log.isDebugEnabled() && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.XACML_RESPONSE)) {
log.debug("XACML Response : " + xacmlResponse);
}
return xacmlResponse;
}
/**
* Evaluates the given XACML request and returns the Response that the EntitlementEngine will
* hand back to the PEP. PEP needs construct the XACML request before sending it to the
* EntitlementEngine
*
* @param xacmlRequest XACML request as String
* @return XACML response as String
* @throws org.wso2.balana.ParsingException throws
* @throws org.wso2.carbon.identity.entitlement.EntitlementException throws
*/
public String evaluate(String xacmlRequest) throws EntitlementException, ParsingException {
if (log.isDebugEnabled() && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.XACML_REQUEST)) {
log.debug("XACML Request : " + xacmlRequest);
}
String xacmlResponse;
if ((xacmlResponse = getFromCache(xacmlRequest, false)) != null) {
if (log.isDebugEnabled() && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.XACML_RESPONSE)) {
log.debug("XACML Response : " + xacmlResponse);
}
return xacmlResponse;
}
Map<PIPExtension, Properties> extensions = EntitlementServiceComponent.getEntitlementConfig()
.getExtensions();
if (extensions != null && !extensions.isEmpty()) {
PolicyRequestBuilder policyRequestBuilder = new PolicyRequestBuilder();
Element xacmlRequestElement = policyRequestBuilder.getXacmlRequest(xacmlRequest);
AbstractRequestCtx requestCtx = RequestCtxFactory.getFactory().
getRequestCtx(xacmlRequestElement);
Set<PIPExtension> pipExtensions = extensions.keySet();
for (PIPExtension pipExtension : pipExtensions) {
pipExtension.update(requestCtx);
}
ResponseCtx responseCtx = pdp.evaluate(requestCtx);
xacmlResponse = responseCtx.encode();
} else {
xacmlResponse = pdp.evaluate(xacmlRequest);
}
addToCache(xacmlRequest, xacmlResponse, false);
if (log.isDebugEnabled() && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.XACML_RESPONSE)) {
log.debug("XACML Response : " + xacmlResponse);
}
return xacmlResponse;
}
/**
* Evaluates XACML request directly. This is used by advance search module.
* Therefore caching and logging has not be implemented for this
*
* @param requestCtx Balana Object model for request
* @return ResponseCtx Balana Object model for response
*/
public ResponseCtx evaluateByContext(AbstractRequestCtx requestCtx) {
return pdp.evaluate(requestCtx);
}
/**
* Evaluates the given XACML request and returns the Response that the EntitlementEngine will
* hand back to the PEP. Here PEP does not need construct the XACML request before sending it to the
* EntitlementEngine. Just can send the single attribute value. But here default attribute ids and data types
* are used
*
* @param subject subject
* @param resource resource
* @param action action
* @param environment environment
* @return XACML request as String object
* @throws Exception throws, if fails
*/
public String evaluate(String subject, String resource, String action, String[] environment)
throws Exception {
String environmentValue = null;
if (environment != null && environment.length > 0) {
environmentValue = environment[0];
}
String response;
String request = (subject != null ? subject : "") + (resource != null ? resource : "") +
(action != null ? action : "") + (environmentValue != null ? environmentValue : "");
if ((response = getFromCache(request, true)) != null) {
if (log.isDebugEnabled() && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.XACML_REQUEST)) {
log.debug("XACML Request : " + EntitlementUtil.
createSimpleXACMLRequest(subject, resource, action, environmentValue));
}
if (log.isDebugEnabled() && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.XACML_RESPONSE)) {
log.debug("XACML Response : " + response);
}
return response;
}
String requestAsString = EntitlementUtil.createSimpleXACMLRequest(subject, resource, action, environmentValue);
if (log.isDebugEnabled() && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.XACML_REQUEST)) {
log.debug("XACML Request : " + requestAsString);
}
response = pdp.evaluate(requestAsString);
addToCache(request, response, true);
if (log.isDebugEnabled() && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.XACML_RESPONSE)) {
log.debug("XACML Response : " + response);
}
return response;
}
/**
* This method is returns the registry based policy finder for current tenant
*
* @return RegistryBasedPolicyFinder
*/
public PolicyFinder getPapPolicyFinder() {
return papPolicyFinder;
}
/**
* This method returns the carbon based attribute finder for the current tenant
*
* @return CarbonAttributeFinder
*/
public CarbonAttributeFinder getCarbonAttributeFinder() {
return carbonAttributeFinder;
}
/**
* This method returns the carbon based resource finder for the current tenant
*
* @return CarbonResourceFinder
*/
public CarbonResourceFinder getCarbonResourceFinder() {
return carbonResourceFinder;
}
/**
* This method returns the carbon based policy finder for the current tenant
*
* @return CarbonPolicyFinder
*/
public PolicyFinder getCarbonPolicyFinder() {
return carbonPolicyFinder;
}
/**
* get entry from decision caching
*
* @param request XACML request as String
* @param simpleCache whether using simple cache or not
* @return XACML response as String
*/
private String getFromCache(String request, boolean simpleCache) {
if (pdpDecisionCacheEnable) {
String tenantRequest = tenantId + "+" + request;
String decision;
//There is no any local cache hereafter and always get from distribute cache if there.
/*if (DecisionInvalidationCache.getInstance().isInvalidate()) {
decisionCache.clearCache();
simpleDecisionCache.clearCache();
}*/
if (simpleCache) {
decision = simpleDecisionCache.getFromCache(tenantRequest);
} else {
decision = decisionCache.getFromCache(tenantRequest);
}
return decision;
}
if (log.isDebugEnabled()) {
log.debug("PDP Decision Caching is disabled");
}
return null;
}
/**
* put entry in to cache
*
* @param request XACML request as String
* @param response XACML response as String
* @param simpleCache whether using simple cache or not
*/
private void addToCache(String request, String response, boolean simpleCache) {
if (pdpDecisionCacheEnable) {
String tenantRequest = tenantId + "+" + request;
if (simpleCache) {
simpleDecisionCache.addToCache(tenantRequest, response);
} else {
decisionCache.addToCache(tenantRequest, response);
}
} else {
if (log.isDebugEnabled()) {
log.debug("PDP Decision Caching is disabled");
}
}
}
/**
* Helper method to init engine
*/
private void setUpAttributeFinders() {
// Creates carbon attribute finder instance and init it
carbonAttributeFinder = new CarbonAttributeFinder(tenantId);
carbonAttributeFinder.init();
// Now setup attribute finder modules for the current date/time and
// AttributeSelectors (selectors are optional, but this project does
// support a basic implementation)
CurrentEnvModule envAttributeModule = new CurrentEnvModule();
SelectorModule selectorAttributeModule = new SelectorModule();
attributeModules.add(carbonAttributeFinder);
attributeModules.add(envAttributeModule);
attributeModules.add(selectorAttributeModule);
for (AttributeFinderModule module : balana.getPdpConfig().getAttributeFinder().getModules()) {
if (module instanceof CurrentEnvModule || module instanceof SelectorModule) {
continue;
}
attributeModules.add(module);
}
}
/**
* Helper method to init engine
*/
private void setUpResourceFinders() {
carbonResourceFinder = new CarbonResourceFinder(tenantId);
carbonResourceFinder.init();
resourceModules.add(carbonResourceFinder);
for (ResourceFinderModule module : balana.getPdpConfig().getResourceFinder().getModules()) {
resourceModules.add(module);
}
}
/**
* Returns instance of policy search
*
* @return <code>PolicySearch</code>
*/
public PolicySearch getPolicySearch() {
return policySearch;
}
private void setUPPolicyFinder() {
carbonPolicyFinder = new PolicyFinder();
Set<PolicyFinderModule> policyModules = new HashSet<PolicyFinderModule>();
CarbonPolicyFinder tmpCarbonPolicyFinder = new CarbonPolicyFinder();
policyModules.add(tmpCarbonPolicyFinder);
carbonPolicyFinder.setModules(policyModules);
carbonPolicyFinder.init();
}
}