/*
* 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 java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
import org.wso2.carbon.base.ServerConfiguration;
import org.wso2.carbon.identity.base.IdentityException;
import org.wso2.carbon.identity.entitlement.pip.CarbonAttributeFinder;
import org.wso2.carbon.identity.entitlement.policy.PolicyReader;
import org.wso2.carbon.identity.entitlement.policy.PolicyStoreReader;
import org.wso2.carbon.identity.entitlement.policy.PolicyStore;
import org.wso2.carbon.identity.entitlement.policy.finder.RegistryBasedPolicyFinder;
import com.sun.xacml.PDP;
import com.sun.xacml.PDPConfig;
import com.sun.xacml.ParsingException;
import com.sun.xacml.ctx.RequestCtx;
import com.sun.xacml.ctx.ResponseCtx;
import com.sun.xacml.finder.AttributeFinder;
import com.sun.xacml.finder.PolicyFinder;
import com.sun.xacml.finder.PolicyFinderModule;
import com.sun.xacml.finder.impl.CurrentEnvModule;
import com.sun.xacml.finder.impl.SelectorModule;
import org.wso2.carbon.registry.core.Registry;
public class EntitlementEngine {
private RegistryBasedPolicyFinder registryModule;
private PDP pdp;
private static volatile EntitlementEngine engine;
private static final Object lock = new Object();
private static final String PDP_DECISION_CACHING_INTERVAL = "pdpDecisionCachingInterval";
private int pdpDecisionCachingInterval = -1;
private Map<String, PolicyDecision> decisionCache = new ConcurrentHashMap<String, PolicyDecision>();
private static ConcurrentHashMap<String, EntitlementEngine> entitlementEngines =
new ConcurrentHashMap<String, EntitlementEngine>();
private static Log log = LogFactory.getLog(EntitlementEngine.class);
/**
* Get a EntitlementEngine instance for that tenant. This method will return an EntitlementEngine
* instance if exists, or creates a new one
* @param registry Governance Registry instance of the corresponding tenant
* @param tenantId Tenant ID of corresponding tenant
* @return EntitlementEngine instance for that tenant
* @throws org.wso2.carbon.identity.base.IdentityException throws IdentityException
*/
public static EntitlementEngine getInstance(Registry registry, int tenantId)
throws IdentityException {
if (!entitlementEngines.containsKey(Integer.toString(tenantId))) {
entitlementEngines.put(Integer.toString(tenantId), new EntitlementEngine(registry, tenantId));
}
return entitlementEngines.get(Integer.toString(tenantId));
}
private EntitlementEngine(Registry registry, int tenantId) throws IdentityException {
PolicyFinder policyFinder = null;
Set<PolicyFinderModule> policyModules = null;
// Setup the PolicyFinder that the EntitlementEngine will use
policyFinder = new PolicyFinder();
registryModule = new RegistryBasedPolicyFinder(new PolicyStoreReader(new PolicyStore(
registry)), tenantId);
policyModules = new HashSet<PolicyFinderModule>();
// Add all policy finders - we only have RegistryBasedPolicyFinder
policyModules.add(registryModule);
policyFinder.setModules(policyModules);
// init policy reader
PolicyReader.getInstance(null, policyFinder);
// 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();
// Setup the AttributeFinder just like we setup the PolicyFinder. Note
// that unlike with the policy finder, the order matters here.
AttributeFinder attributeFinder = new AttributeFinder();
List<Object> attributeModules = new ArrayList<Object>();
attributeModules.add(envAttributeModule);
attributeModules.add(selectorAttributeModule);
attributeModules.add(new CarbonAttributeFinder());
attributeFinder.setModules(attributeModules);
String cacheInterval = ServerConfiguration.getInstance().getFirstProperty(
PDP_DECISION_CACHING_INTERVAL);
if (cacheInterval != null) {
pdpDecisionCachingInterval = Integer.parseInt(cacheInterval);
}
// Finally, initialize
pdp = new PDP(new PDPConfig(attributeFinder, policyFinder, null));
}
/**
* 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 xamlRequest
* XACML request as an Element
* @return Response
* @throws com.sun.xacml.ParsingException
* @throws java.io.IOException
*/
public ResponseCtx evaluate(Element xamlRequest) throws IOException, ParsingException {
RequestCtx request = RequestCtx.getInstance(xamlRequest);
ResponseCtx response = null;
if ((response = getFromCache(request)) != null) {
return response;
}
// evaluate the request
response = pdp.evaluate(request);
addToCache(request, response);
return response;
}
/**
* 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 request
* @return
* @throws IOException
* @throws ParsingException
*/
public ResponseCtx evaluate(RequestCtx request) throws IOException, ParsingException {
ResponseCtx response = null;
if ((response = getFromCache(request)) != null) {
return response;
}
// evaluate the request
response = pdp.evaluate(request);
addToCache(request, response);
return response;
}
/**
* This method is returns the registry based policy finder for current tenant
*
* @return RegistryBasedPolicyFinder
*/
public RegistryBasedPolicyFinder getRegistryModule() {
return registryModule;
}
private ResponseCtx getFromCache(RequestCtx request) {
if (pdpDecisionCachingInterval > 0) {
OutputStream xml = new ByteArrayOutputStream();
request.encode(xml);
PolicyDecision decision = decisionCache.get(xml.toString());
if (decision != null
&& (decision.getCachedTime() + new Long(pdpDecisionCachingInterval).longValue() > Calendar
.getInstance().getTimeInMillis())) {
if (log.isDebugEnabled()) {
log.debug("PDP Decision Cache Hit");
}
return decision.getResponse();
} else {
if (log.isDebugEnabled()) {
log.debug("PDP Decision Cache Miss");
}
decisionCache.remove(xml.toString());
return null;
}
}
if (log.isDebugEnabled()) {
log.debug("PDP Decision Caching Disabled");
}
return null;
}
private void addToCache(RequestCtx request, ResponseCtx response) {
if (pdpDecisionCachingInterval > 0) {
PolicyDecision decision = new PolicyDecision();
OutputStream xml = new ByteArrayOutputStream();
request.encode(xml);
decision.setCachedTime(Calendar.getInstance().getTimeInMillis());
decision.setResponse(response);
decisionCache.put(xml.toString(), decision);
if (log.isDebugEnabled()) {
log.debug("PDP Decision Cache Updated");
}
} else {
if (log.isDebugEnabled()) {
log.debug("PDP Decision Caching Disabled");
}
}
}
/**
* Clears the decision cache.
*/
public void clearDecisionCache() {
decisionCache.clear();
}
}