org.apache.openaz.xacml.std.pip.engines.ldap.ConfigurableLDAPResolver.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.openaz.xacml.std.pip.engines.ldap.ConfigurableLDAPResolver.java

Source

/*
 *  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)));
        }
    }

}