Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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.apache.openaz.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.NamingException; import javax.naming.directory.SearchResult; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.openaz.xacml.api.Attribute; import org.apache.openaz.xacml.api.AttributeValue; import org.apache.openaz.xacml.api.DataType; import org.apache.openaz.xacml.api.DataTypeException; import org.apache.openaz.xacml.api.DataTypeFactory; import org.apache.openaz.xacml.api.pip.PIPEngine; import org.apache.openaz.xacml.api.pip.PIPException; import org.apache.openaz.xacml.api.pip.PIPFinder; import org.apache.openaz.xacml.api.pip.PIPRequest; import org.apache.openaz.xacml.api.pip.PIPResponse; import org.apache.openaz.xacml.std.StdAttribute; import org.apache.openaz.xacml.std.datatypes.DataTypes; import org.apache.openaz.xacml.std.pip.StdPIPRequest; import org.apache.openaz.xacml.std.pip.engines.Configurables; import org.apache.openaz.xacml.util.FactoryException; 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; 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 Log logger = LogFactory.getLog(this.getClass()); private String defaultIssuer; private String id; private String base; private String filter; private Map<String, PIPRequest> baseParameters; private Map<String, PIPRequest> filterParameters; private Map<String, PIPRequest> 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<String> baseParametersNames = prepareVelocityTemplate(this.base); Set<String> 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<String> 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<String, PIPRequest> templateParameters, final PIPFinder pipFinder) throws PIPException { StringWriter out = new StringWriter(); VelocityContext vctx = new VelocityContext(); EventCartridge vec = new EventCartridge(); VelocityParameterWriter writer = new VelocityParameterWriter(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, 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<Attribute> 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<AttributeValue<?>> 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, pipFinder); } public void setBase(String base) throws PIPException { Set<String> 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, pipFinder); } public void setFilterString(String filter) throws PIPException { Set<String> 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<AttributeValue<?>> 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<AttributeValue<?>>(); // 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<Attribute> decodeResult(SearchResult searchResult) throws PIPException { List<Attribute> attributes = new ArrayList<Attribute>(); for (Map.Entry<String, PIPRequest> 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)+\\}"); @Override 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<String> parameters = new HashSet<String>(); @Override public Object referenceInsert(String theReference, Object theValue) { String param = (String) super.referenceInsert(theReference, theValue); parameters.add(param); return ""; } } private class VelocityParameterWriter extends VelocityParameterHandler { private PIPFinder finder; private Map<String, PIPRequest> parameters; public VelocityParameterWriter(PIPFinder finder, Map<String, PIPRequest> parameters) { this.finder = finder; this.parameters = parameters; } @Override 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.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<PIPRequest> attributes) { for (String key : this.filterView.keySet()) { attributes.add(new StdPIPRequest(this.filterView.get(key))); } } @Override public void attributesProvided(Collection<PIPRequest> 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))); } } }