/*
* 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.pip;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.balana.ParsingException;
import org.wso2.balana.attr.AttributeValue;
import org.wso2.balana.attr.BagAttribute;
import org.wso2.balana.cond.EvaluationResult;
import org.wso2.balana.ctx.EvaluationCtx;
import org.wso2.balana.ctx.Status;
import org.wso2.balana.finder.AttributeFinderModule;
import org.wso2.carbon.identity.entitlement.EntitlementUtil;
import org.wso2.carbon.identity.entitlement.PDPConstants;
import org.wso2.carbon.identity.entitlement.cache.PIPAttributeCache;
import org.wso2.carbon.identity.entitlement.internal.EntitlementServiceComponent;
import org.wso2.carbon.identity.entitlement.pdp.EntitlementEngine;
import javax.xml.transform.TransformerException;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* CarbonAttributeFinder registers with sun-xacml engine as an AttributeFinderModule and delegate
* functionality to the attribute handlers registered with it self.
* <p/>
* Whenever the XACML engine finds a missing attribute in the XACML request - it will call the
* findAttribute() method of this class.
*/
public class CarbonAttributeFinder extends AttributeFinderModule {
private Map<String, List<PIPAttributeFinder>> attrFinders = new HashMap<String, List<PIPAttributeFinder>>();
private static Log log = LogFactory.getLog(CarbonAttributeFinder.class);
private PIPAttributeCache attributeFinderCache = null;
protected int tenantId;
public CarbonAttributeFinder(int tenantId) {
this.tenantId = tenantId;
}
/**
* Registers PIP attribute handlers with the PDP against their supported attributes. This PIP
* attribute handlers are picked from pip-config.xml file - which should be inside
* [CARBON_HOME]\repository\conf.
*/
public void init() {
Map<PIPAttributeFinder, Properties> designators = EntitlementServiceComponent.getEntitlementConfig()
.getDesignators();
Properties properties = EntitlementServiceComponent.getEntitlementConfig().getEngineProperties();
if ("true".equals(properties.getProperty(PDPConstants.ATTRIBUTE_CACHING))) {
int attributeCachingInterval = -1;
String cacheInterval = properties.getProperty(PDPConstants.ATTRIBUTE_CACHING_INTERVAL);
if (cacheInterval != null) {
try {
attributeCachingInterval = Integer.parseInt(cacheInterval.trim());
} catch (Exception e) {
//ignore
}
}
attributeFinderCache = new PIPAttributeCache(attributeCachingInterval);
}
// clear decision cache
if (designators != null && !designators.isEmpty()) {
Set<PIPAttributeFinder> pipAttributeFinders = designators.keySet();
for (Iterator iterator = pipAttributeFinders.iterator(); iterator.hasNext(); ) {
PIPAttributeFinder pipAttributeFinder = (PIPAttributeFinder) iterator.next();
Set<String> attrs = pipAttributeFinder.getSupportedAttributes();
if (attrs != null) {
for (Iterator attrsIter = attrs.iterator(); attrsIter.hasNext(); ) {
String attr = (String) attrsIter.next();
if (attrFinders.containsKey(attr)) {
List<PIPAttributeFinder> finders = attrFinders.get(attr);
if (!finders.contains(pipAttributeFinder)) {
finders.add(pipAttributeFinder);
if (log.isDebugEnabled()) {
log.debug(String
.format("PIP attribute handler %1$s registered for the " +
"supported attribute %2$s",
pipAttributeFinder.getClass(), attr));
}
}
} else {
List<PIPAttributeFinder> finders = new ArrayList<PIPAttributeFinder>();
finders.add(pipAttributeFinder);
attrFinders.put(attr, finders);
if (log.isDebugEnabled()) {
log.debug(String
.format("PIP attribute handler %1$s registered for the supported " +
"attribute %2$s",
pipAttributeFinder.getClass(), attr));
}
}
}
}
}
}
}
/*
* (non-Javadoc)
*
* @see org.wso2.balana.finder.AttributeFinderModule#findAttribute(java.net.URI, java.net.URI,
* java.net.URI, java.net.URI, org.wso2.balana.EvaluationCtx, int)
*/
public EvaluationResult findAttribute(URI attributeType, URI attributeId, String issuer,
URI category, EvaluationCtx context) {
List<AttributeValue> attrBag = new ArrayList<AttributeValue>();
// Get the list of attribute finders who are registered with this particular attribute.
List<PIPAttributeFinder> finders = attrFinders.get(attributeId.toString());
if (finders == null || finders.size() == 0) {
finders = attrFinders.get(attributeId.toString());
if (finders == null || finders.size() == 0) {
if (log.isDebugEnabled()) {
log.debug("No attribute designators defined for the attribute "
+ attributeId.toString());
}
return new EvaluationResult(BagAttribute.createEmptyBag(attributeType));
}
}
try {
for (Iterator iterator = finders.iterator(); iterator.hasNext(); ) {
PIPAttributeFinder pipAttributeFinder = (PIPAttributeFinder) iterator.next();
if (log.isDebugEnabled()) {
log.debug(String.format(
"Finding attributes with the PIP attribute handler %1$s",
pipAttributeFinder.getClass()));
}
Set<String> attrs = null;
String key = null;
if (attributeFinderCache != null && !pipAttributeFinder.overrideDefaultCache()) {
key = attributeType.toString() + attributeId.toString() + category.toString() +
encodeContext(context);
if (issuer != null) {
key += issuer;
}
if (key != null) {
attrs = attributeFinderCache.getFromCache(tenantId, key);
}
}
if (attrs == null) {
attrs = pipAttributeFinder.getAttributeValues(attributeType, attributeId, category,
issuer, context);
if (attributeFinderCache != null && key != null
&& !pipAttributeFinder.overrideDefaultCache()) {
attributeFinderCache.addToCache(tenantId, key, attrs);
}
}
if (attrs != null) {
for (Iterator iterAttr = attrs.iterator(); iterAttr.hasNext(); ) {
final String attr = (String) iterAttr.next();
AttributeValue attribute = EntitlementUtil.
getAttributeValue(attr, attributeType.toString());
attrBag.add(attribute);
}
}
}
} catch (ParsingException e) {
log.error("Error while parsing attribute values from EvaluationCtx : " + e);
ArrayList<String> code = new ArrayList<String>();
code.add(Status.STATUS_MISSING_ATTRIBUTE);
Status status = new Status(code,
"Error while parsing attribute values from EvaluationCtx : " + e.getMessage());
return new EvaluationResult(status);
} catch (ParseException e) {
e.printStackTrace();
log.error("Error while parsing attribute values from EvaluationCtx : " + e);
ArrayList<String> code = new ArrayList<String>();
code.add(Status.STATUS_MISSING_ATTRIBUTE);
Status status = new Status(code,
"Error while parsing attribute values from EvaluationCtx : " + e.getMessage());
return new EvaluationResult(status);
} catch (URISyntaxException e) {
log.error("Error while parsing attribute values from EvaluationCtx : " + e);
ArrayList<String> code = new ArrayList<String>();
code.add(Status.STATUS_MISSING_ATTRIBUTE);
Status status = new Status(code,
"Error while parsing attribute values from EvaluationCtx :" + e.getMessage());
return new EvaluationResult(status);
} catch (Exception e) {
log.error("Error while retrieving attribute values from PIP attribute finder : " + e);
ArrayList<String> code = new ArrayList<String>();
code.add(Status.STATUS_MISSING_ATTRIBUTE);
Status status = new Status(code, "Error while retrieving attribute values from PIP"
+ " attribute finder : " + e.getMessage());
return new EvaluationResult(status);
}
return new EvaluationResult(new BagAttribute(attributeType, attrBag));
}
/*
* (non-Javadoc)
*
* @see org.wso2.balana.finder.AttributeFinderModule#isDesignatorSupported()
*/
public boolean isDesignatorSupported() {
return true;
}
/*
* (non-Javadoc)
*
* @see org.wso2.balana.finder.AttributeFinderModule#getSupportedIds()
*/
public Set getSupportedIds() {
return null;
}
/**
* Registers PIP attribute handlers are initialized when the server is start-up. This method can
* be used to refresh all attribute finders internally. refreshSupportedAttribute() method must be
* implemented within the PIP attribute finder to perform this operation. Also this uses to find newly
* defined attributes, attribute caches are would not be cleared.
*
* @throws Exception throws then initialization of attribute finders are failed
*/
private void refreshAttributeFindersForNewAttributeId() throws Exception {
Map<PIPAttributeFinder, Properties> designators = EntitlementServiceComponent.getEntitlementConfig()
.getDesignators();
if (designators != null && !designators.isEmpty()) {
Set<Map.Entry<PIPAttributeFinder, Properties>> attributeFinders = designators.entrySet();
for (Map.Entry<PIPAttributeFinder, Properties> attributeFinder : attributeFinders) {
attributeFinder.getKey().init(attributeFinder.getValue());
}
init();
}
}
/**
* Clears attribute cache
*/
public void clearAttributeCache() {
if (attributeFinderCache != null) {
attributeFinderCache.clearCache();
// clear decision cache
EntitlementEngine.getInstance().clearDecisionCache();
}
}
/**
* Converts DOM object to String. This is a helper method for creating cache key
*
* @param evaluationCtx EvaluationCtx
* @return String Object
* @throws TransformerException Exception throws if fails
*/
private String encodeContext(EvaluationCtx evaluationCtx) throws TransformerException {
OutputStream stream = new ByteArrayOutputStream();
evaluationCtx.getRequestCtx().encode(stream);
return stream.toString();
}
}