com.att.research.xacml.std.pip.engines.ldap.ConfigurableLDAPResolver Maven / Gradle / Ivy
The newest version!
/*
*
* Copyright (c) 2014,2019 AT&T Knowledge Ventures
* SPDX-License-Identifier: MIT
*/
package com.att.research.xacml.std.pip.engines.ldap;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//import javax.naming.directory.Attribute;
import javax.naming.NamingException;
import javax.naming.directory.SearchResult;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.event.EventCartridge;
import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.att.research.xacml.api.Attribute;
import com.att.research.xacml.api.AttributeValue;
import com.att.research.xacml.api.DataType;
import com.att.research.xacml.api.DataTypeException;
import com.att.research.xacml.api.DataTypeFactory;
import com.att.research.xacml.api.pip.PIPEngine;
import com.att.research.xacml.api.pip.PIPException;
import com.att.research.xacml.api.pip.PIPFinder;
import com.att.research.xacml.api.pip.PIPRequest;
import com.att.research.xacml.api.pip.PIPResponse;
import com.att.research.xacml.std.StdAttribute;
import com.att.research.xacml.std.datatypes.DataTypes;
import com.att.research.xacml.std.pip.StdPIPRequest;
import com.att.research.xacml.std.pip.engines.Configurables;
import com.att.research.xacml.util.FactoryException;
public class ConfigurableLDAPResolver implements LDAPResolver {
private static DataTypeFactory dataTypeFactory = null;
static {
try {
dataTypeFactory = DataTypeFactory.newInstance();
}
catch (FactoryException fx) {
throw new RuntimeException(fx);
}
Velocity.setProperty( "runtime.log.logsystem.log4j.logger", "MAIN_LOG" );
Velocity.init();
}
private Logger logger = LoggerFactory.getLogger(this.getClass());
private String defaultIssuer;
private String id;
private String base;
private String filter;
private Map baseParameters;
private Map filterParameters;
private Map filterView;
public ConfigurableLDAPResolver() {
}
@Override
public void configure(String id, Properties properties, String defaultIssuer) throws PIPException {
/*
* Save these values
*/
this.id = id;
this.defaultIssuer = defaultIssuer;
this.base = properties.getProperty(id + ".base");
this.filter = properties.getProperty(id + ".filter");
Set baseParametersNames = prepareVelocityTemplate(this.base);
Set filterParametersNames = prepareVelocityTemplate(this.filter);
this.baseParameters = Configurables.getPIPRequestMap(id + ".base", "parameters", properties, null);
this.filterParameters = Configurables.getPIPRequestMap(id + ".filter", "parameters", properties, null);
//make sure we have all required parameters
if (!this.baseParameters.keySet().containsAll(baseParametersNames)) {
throw new PIPException("The 'base' template contains parameters that were not specified in its map.");
}
if (!this.filterParameters.keySet().containsAll(filterParametersNames)) {
throw new PIPException("The 'filter' template contains parameters that were not specified in its map.");
}
this.filterView = Configurables.getPIPRequestMap(id + ".filter", "view", properties, defaultIssuer);
if (this.logger.isTraceEnabled()) {
this.logger.trace("(" + id + ") " +
"\nbase '" + this.base + "', parameters " + this.baseParameters +
"\nfilter '" + this.filter + "', parameters " + this.filterParameters + ", view " + this.filterView);
}
}
public void store(String id, Properties properties) throws PIPException {
properties.setProperty(id + ".base", this.base);
properties.setProperty(id + ".filter", this.filter);
Configurables.setPIPRequestMap(this.baseParameters,
id + ".base", "parameters", properties);
Configurables.setPIPRequestMap(this.filterParameters,
id + ".filter", "parameters", properties);
Configurables.setPIPRequestMap(this.filterView,
id + ".filter", "view", properties);
}
/*
* @return the set of parameters names required by the given velocity template
*/
private Set prepareVelocityTemplate(String template)
throws PIPException {
VelocityContext vctx = new VelocityContext();
EventCartridge vec = new EventCartridge();
VelocityParameterReader reader = new VelocityParameterReader();
vec.addEventHandler(reader);
vec.attachToContext(vctx);
try {
Velocity.evaluate(vctx, new StringWriter(),
"LdapResolver", template);
}
catch (ParseErrorException pex) {
throw new PIPException(
"Velocity template preparation failed",pex);
}
catch (MethodInvocationException mix) {
throw new PIPException(
"Velocity template preparation failed",mix);
}
catch (ResourceNotFoundException rnfx) {
throw new PIPException(
"Velocity template preparation failed",rnfx);
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("(" + id + ") " + template + " with parameters " + reader.parameters);
}
return reader.parameters;
}
private String evaluateVelocityTemplate(String template,
final Map templateParameters,
final PIPEngine pipEngine,
final PIPFinder pipFinder)
throws PIPException {
StringWriter out = new StringWriter();
VelocityContext vctx = new VelocityContext();
EventCartridge vec = new EventCartridge();
VelocityParameterWriter writer = new VelocityParameterWriter(
pipEngine, pipFinder, templateParameters);
vec.addEventHandler(writer);
vec.attachToContext(vctx);
try {
Velocity.evaluate(vctx, out,
"LdapResolver", template);
}
catch (ParseErrorException pex) {
throw new PIPException(
"Velocity template evaluation failed",pex);
}
catch (MethodInvocationException mix) {
throw new PIPException(
"Velocity template evaluation failed",mix);
}
catch (ResourceNotFoundException rnfx) {
throw new PIPException(
"Velocity template evaluation failed",rnfx);
}
this.logger.warn("(" + id + ") " + " template yields " + out.toString());
return out.toString();
}
private Object evaluatePIPRequest(PIPRequest pipRequest,
PIPEngine pipEngine,
PIPFinder pipFinder)
throws PIPException {
if (this.logger.isTraceEnabled()) {
this.logger.trace("(" + id + ") " + pipRequest);
}
PIPResponse pipResponse = pipFinder.getMatchingAttributes(pipRequest, null);
if (pipResponse.getStatus() == null || pipResponse.getStatus().isOk()) {
Collection listAttributes = pipResponse.getAttributes();
if (listAttributes.size() > 0) {
if (listAttributes.size() > 1) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("(" + id + ") " + "PIPFinder returned more than one Attribute for " + pipRequest);
}
throw new PIPException("PIPFinder returned more than one Attribute for " + pipRequest.toString());
}
Collection> listAttributeValuesReturned = listAttributes.iterator().next().getValues();
if (listAttributeValuesReturned.size() > 0) {
if (listAttributeValuesReturned.size() > 1) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("(" + id + ") " + "PIPFinder returned more than one AttributeValue for " + pipRequest);
}
return null;
}
AttributeValue> attributeValue = listAttributeValuesReturned.iterator().next();
//this is to hoping the string representation of the value is accurate
try {
return DataTypes.DT_STRING.convert(attributeValue.getValue());
}
catch (DataTypeException dtx) {
throw new PIPException("Fauiled to extract attribute value", dtx);
}
}
}
}
return null;
}
@Override
public String getBase(PIPEngine pipEngine,
PIPRequest pipRequest,
PIPFinder pipFinder) throws PIPException {
if (!filterView.containsValue(pipRequest)) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("(" + id + ") " + pipRequest + " not in " + filterView);
}
return null;
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("(" + id + ") " + pipRequest);
}
return evaluateVelocityTemplate(this.base, this.baseParameters,
pipEngine, pipFinder);
}
public void setBase(String base) throws PIPException {
Set baseParametersNames = prepareVelocityTemplate(base);
//make sure we have all required parameters
if (!this.baseParameters.keySet().containsAll(baseParametersNames)) {
throw new PIPException("The 'base' template contains parameters that were not specified in its map.");
}
this.base = base;
}
@Override
public String getFilterString(PIPEngine pipEngine, PIPRequest pipRequest,
PIPFinder pipFinder) throws PIPException {
if (this.logger.isTraceEnabled()) {
this.logger.trace("(" + id + ") " + pipRequest);
}
if (!filterView.containsValue(pipRequest)) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("(" + id + ") " + "request " + pipRequest + " not in " + filterView);
}
return null;
}
return evaluateVelocityTemplate(this.filter, this.filterParameters,
pipEngine, pipFinder);
}
public void setFilterString(String filter) throws PIPException {
Set filterParametersNames = prepareVelocityTemplate(filter);
//make sure we have all required parameters
if (!this.filterParameters.keySet().containsAll(filterParametersNames)) {
throw new PIPException("The 'filter' template contains parameters that were not specified in its map.");
}
this.filter = filter;
}
private Attribute decodeResultValue(SearchResult searchResult,
String view,
PIPRequest viewRequest) {
AttributeValue> attributeValue = null;
Collection> attributeMultiValue = null;
DataType> dataType = null;
this.logger.warn("(" + id + ") " + "SearchResult attributes: " + searchResult.getAttributes());
try {
dataType = dataTypeFactory.getDataType(
viewRequest.getDataTypeId());
if (dataType == null) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("(" + id + ") " + "Unknown data type in " + viewRequest);
}
return null;
}
if ("dn".equalsIgnoreCase(view)) {
attributeValue = dataType.createAttributeValue(
searchResult.getNameInNamespace());
}
else {
javax.naming.directory.Attribute dirAttr =
searchResult.getAttributes().get(view);
if (dirAttr != null) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("(" + id + ") " + "directory attribute '" + view + "' value is '" + dirAttr + "'");
}
//we could guide this more elaborately by object class ..
if (dirAttr.size() == 1) {
attributeValue = dataType.createAttributeValue(
dirAttr.get().toString());
} else {
if (this.logger.isTraceEnabled()) {
this.logger.trace("(" + id + ") " + "SearchResult yields a multi-valued '" + view+ "'");
}
attributeMultiValue = new HashSet>();
//we should
for (int i = 0; i < dirAttr.size(); i++) {
attributeMultiValue.add(
dataType.createAttributeValue(
dirAttr.get().toString()));
}
}
}
else {
this.logger.warn("(" + id + ") " + "SearchResult did not provide a value for '" + view+ "'");
return null;
}
}
}
catch (DataTypeException dtx) {
this.logger.error("(" + id + ") " + "Failed to decode search result", dtx);
return null;
}
catch (NamingException nx) {
this.logger.error("(" + id + ") " + "Failed to decode search result", nx);
return null;
}
Attribute attr = null;
if (attributeMultiValue == null) {
attr = new StdAttribute(viewRequest.getCategory(),
viewRequest.getAttributeId(),
attributeValue,
viewRequest.getIssuer(),
false);
}
else {
attr = new StdAttribute(viewRequest.getCategory(),
viewRequest.getAttributeId(),
attributeMultiValue,
viewRequest.getIssuer(),
false);
}
this.logger.warn("(" + id + ") " + " providing attribute " + attr);
return attr;
}
@Override
public List decodeResult(SearchResult searchResult)
throws PIPException {
List attributes = new ArrayList();
for (Map.Entry viewEntry: this.filterView.entrySet()) {
Attribute attribute = this.decodeResultValue(searchResult, viewEntry.getKey(), viewEntry.getValue());
if (attribute != null) {
attributes.add(attribute);
}
}
return attributes;
}
private class VelocityParameterHandler implements ReferenceInsertionEventHandler {
/* velocity parameter pattern: we're just trying to extract the name */
private Pattern vpp = Pattern.compile("\\{(\\w)+\\}");
public Object referenceInsert(String theReference, Object theValue) {
/* unfortunately Velocity does not give us simply the variable name
but it's whole template representation, i.e. ${var_name} or derivatives.
We look for whatever is between { and } */
Matcher vvm = vpp.matcher(theReference);
String param = null;
// Check all occurance
if (vvm.find()) {
String vv = vvm.group();
param = vv.substring(1,vv.length()-1);
}
else {
//variable name pattern not right?
param = "";
}
if (ConfigurableLDAPResolver.this.logger.isTraceEnabled()) {
ConfigurableLDAPResolver.this.logger.trace("(" + id + ") " + "Velocity parameter: " + param);
}
return param;
}
}
/* */
private class VelocityParameterReader extends VelocityParameterHandler {
private Set parameters = new HashSet();
public Object referenceInsert(String theReference,
Object theValue) {
String param = (String)super.referenceInsert(theReference, theValue);
parameters.add(param);
return "";
}
}
private class VelocityParameterWriter extends VelocityParameterHandler {
private PIPEngine engine;
private PIPFinder finder;
private Map parameters;
public VelocityParameterWriter(PIPEngine engine,
PIPFinder finder,
Map parameters) {
this.engine = engine;
this.finder = finder;
this.parameters = parameters;
}
public Object referenceInsert(String theReference,
Object theValue) {
String param = (String)super.referenceInsert(theReference, theValue);
try {
PIPRequest request = parameters.get(param);
if (ConfigurableLDAPResolver.this.logger.isTraceEnabled()) {
ConfigurableLDAPResolver.this.logger.trace("(" + id + ") " + "Velocity parameter: " + param + " requests " + request);
}
if (null == request)
throw new RuntimeException("Parameter '" + param + "' is not available");
Object val = ConfigurableLDAPResolver.this.evaluatePIPRequest(
request, this.engine, this.finder);
if (null != val) {
return val;
}
else {
if (param.startsWith("_")) {
return "*";
}
else {
return null;
}
}
}
catch (PIPException pipx) {
throw new RuntimeException(pipx);
}
}
}
@Override
public void attributesRequired(Collection attributes) {
for (String key : this.filterView.keySet()) {
attributes.add(new StdPIPRequest(this.filterView.get(key)));
}
}
@Override
public void attributesProvided(Collection attributes) {
for (String key : this.filterParameters.keySet()) {
PIPRequest attribute = this.filterParameters.get(key);
attributes.add(new StdPIPRequest(attribute.getCategory(),
attribute.getAttributeId(),
attribute.getDataTypeId(),
(attribute.getIssuer() != null ? attribute.getIssuer() : this.defaultIssuer)));
}
}
}